{"id":"2b2e1eca-98ef-44a2-beb7-ff4a2f722857","entityType":"agent","slug":"clawhub-nickmerwin-buck-mason-stylist-skill","name":"Buck Mason Stylist","canonicalUrl":"https://www.xpersona.co/agent/clawhub-nickmerwin-buck-mason-stylist-skill","canonicalPath":"/agent/clawhub-nickmerwin-buck-mason-stylist-skill","generatedAt":"2026-10-10T23:47:32.860Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T19:57:04.367Z","emptyReason":null},"description":"Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lo... Skill: Buck Mason Stylist Owner: nickmerwin Summary: Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lo... Tags: latest:0.7.2 Version history: v0.7.2 | 2026-05-19T16:51:28.017Z | auto Version 0.7.2 - Adds support and documentation for the agent-driven MPP checkout path via @stripe/link-cli. - Updates required ag","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.3K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17dybwreermy66eaezsvfsgmn85w1pw:buck-mason-stylist-skill","sourceUrl":"https://clawhub.ai/nickmerwin/buck-mason-stylist-skill","homepage":"https://clawhub.ai/nickmerwin/skills/buck-mason-stylist-skill","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/nickmerwin/buck-mason-stylist-skill","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/nickmerwin/skills/buck-mason-stylist-skill","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":62,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lo..."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T19:57:04.367Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T19:57:04.367Z","emptyReason":null},"stars":null,"forks":null,"downloads":1274,"packageName":null,"latestVersion":"0.7.2","tractionLabel":"1.3K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T19:57:04.367Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T19:57:04.367Z","lastCrawledAt":"2026-10-10T19:57:04.367Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T19:57:04.367Z","lastVerifiedAt":null,"highlights":[{"version":"0.7.2","createdAt":"2026-05-19T16:51:28.017Z","changelog":"Version 0.7.2 - Adds support and documentation for the agent-driven MPP checkout path via `@stripe/link-cli`. - Updates required agent environment/config; emphasizes that most workflows run without environment variables. - Removes old or redundant reference files (e.g., cart rules doc). - Revises and clarifies when each workflow path activates and user setup requirements. - Various corrections and updates to documentation to reflect recent feature and workflow changes.","fileCount":48,"zipByteSize":209872},{"version":"0.7.1","createdAt":"2026-05-19T02:57:25.484Z","changelog":"Add Codex support, default gpt-image-2 try-on lookbooks, and Cloudflare voting.","fileCount":48,"zipByteSize":210267},{"version":"0.6.5","createdAt":"2026-05-08T20:33:48.736Z","changelog":"Fix scanner tripwires: strip U+200B from headless-mode.md (ClawScan unicode-control-chars), and move minimal-JPEG fixture bytes out of test .py files into tests/fixtures/minimal.jpg (Static-Analysis obfuscated_code false-positive).","fileCount":42,"zipByteSize":190560},{"version":"0.6.4","createdAt":"2026-05-08T20:21:01.087Z","changelog":"Close ClawScan v0.6.2 'Cascading Failures' Concern: declare lookbook deploy egress (Cloudflare Pages opt-in via wrangler) and split documented-only alternatives (Surge/Netlify/Vercel/Gist/S3/0x0.st) into their own manifest field. Stripe + OpenAI gain opt_in/trigger/via fields. SECURITY.md and CLAUDE.md follow the same split.","fileCount":42,"zipByteSize":189817},{"version":"0.6.2","createdAt":"2026-05-06T06:50:23.832Z","changelog":"## Buck Mason Stylist Skill v0.6.2 Changelog - Updated SKILL.md for improved clarity and completeness. - Minor metadata and documentation improvements. - No functional or behavioral code changes.","fileCount":42,"zipByteSize":187724},{"version":"0.6.1","createdAt":"2026-05-06T06:46:38.963Z","changelog":"- Adds comprehensive automated test coverage, including tests for lookbook HTML generation, profile parsing, calendar event scoring, lookbook validation, and face verification logic. - Introduces a new internal library (`scripts/lib/`) to better organize profile and validation code. - Adds a CLAUDE.md file and updates documentation for maintainers and repo readers. - Refactors lookbook runner logic for improved modularity and testability.","fileCount":42,"zipByteSize":187492},{"version":"0.6.0","createdAt":"2026-05-06T06:18:41.074Z","changelog":"- Adds support for headless lookbook image generation via script update. - Updates references and documentation for improved clarity on image generation and MCP API usage. - Improves configuration in clawhub.json for compatibility. - Enhances guidance in skill metadata and instructions. - Minor edits and consistency improvements to supporting files.","fileCount":34,"zipByteSize":174696},{"version":"0.5.1","createdAt":"2026-05-06T05:49:51.189Z","changelog":"Version 0.5.1 - Improved documentation for headless lookbook and modes (see updates in `references/headless-mode.md`) - Minor script and configuration updates for headless lookbook functionality (`scripts/run-headless-lookbook.py`, `clawhub.json`) - Clarified environment variable usage and requirements in `SKILL.md` - No changes to core agent or customer-facing workflows","fileCount":34,"zipByteSize":172574}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17dybwreermy66eaezsvfsgmn85w1pw:buck-mason-stylist-skill","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T23:47:32.854Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nickmerwin-buck-mason-stylist-skill/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T19:57:04.367Z","emptyReason":null},"readme":"Skill: Buck Mason Stylist\n\nOwner: nickmerwin\n\nSummary: Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lo...\n\nTags: latest:0.7.2\n\nVersion history:\n\nv0.7.2 | 2026-05-19T16:51:28.017Z | auto\n\nVersion 0.7.2\n\n- Adds support and documentation for the agent-driven MPP checkout path via `@stripe/link-cli`.\n- Updates required agent environment/config; emphasizes that most workflows run without environment variables.\n- Removes old or redundant reference files (e.g., cart rules doc).\n- Revises and clarifies when each workflow path activates and user setup requirements.\n- Various corrections and updates to documentation to reflect recent feature and workflow changes.\n\nv0.7.1 | 2026-05-19T02:57:25.484Z | user\n\nAdd Codex support, default gpt-image-2 try-on lookbooks, and Cloudflare voting.\n\nv0.6.5 | 2026-05-08T20:33:48.736Z | user\n\nFix scanner tripwires: strip U+200B from headless-mode.md (ClawScan unicode-control-chars), and move minimal-JPEG fixture bytes out of test .py files into tests/fixtures/minimal.jpg (Static-Analysis obfuscated_code false-positive).\n\nv0.6.4 | 2026-05-08T20:21:01.087Z | user\n\nClose ClawScan v0.6.2 'Cascading Failures' Concern: declare lookbook deploy egress (Cloudflare Pages opt-in via wrangler) and split documented-only alternatives (Surge/Netlify/Vercel/Gist/S3/0x0.st) into their own manifest field. Stripe + OpenAI gain opt_in/trigger/via fields. SECURITY.md and CLAUDE.md follow the same split.\n\nv0.6.2 | 2026-05-06T06:50:23.832Z | auto\n\n## Buck Mason Stylist Skill v0.6.2 Changelog\n\n- Updated SKILL.md for improved clarity and completeness.\n- Minor metadata and documentation improvements.\n- No functional or behavioral code changes.\n\nv0.6.1 | 2026-05-06T06:46:38.963Z | auto\n\n- Adds comprehensive automated test coverage, including tests for lookbook HTML generation, profile parsing, calendar event scoring, lookbook validation, and face verification logic.\n- Introduces a new internal library (`scripts/lib/`) to better organize profile and validation code.\n- Adds a CLAUDE.md file and updates documentation for maintainers and repo readers.\n- Refactors lookbook runner logic for improved modularity and testability.\n\nv0.6.0 | 2026-05-06T06:18:41.074Z | auto\n\n- Adds support for headless lookbook image generation via script update.\n- Updates references and documentation for improved clarity on image generation and MCP API usage.\n- Improves configuration in clawhub.json for compatibility.\n- Enhances guidance in skill metadata and instructions.\n- Minor edits and consistency improvements to supporting files.\n\nv0.5.1 | 2026-05-06T05:49:51.189Z | auto\n\nVersion 0.5.1\n\n- Improved documentation for headless lookbook and modes (see updates in `references/headless-mode.md`)\n- Minor script and configuration updates for headless lookbook functionality (`scripts/run-headless-lookbook.py`, `clawhub.json`)\n- Clarified environment variable usage and requirements in `SKILL.md`\n- No changes to core agent or customer-facing workflows\n\nv0.5.0 | 2026-05-06T05:23:50.455Z | auto\n\n**Summary:**  \nAdds face verification utility, improves image-generation references, and updates documentation for enhanced workflow clarity.\n\n- Added `scripts/verify-face.py` for automated face verification in lookbook generation.\n- Updated references in `image-generation.md` for clearer guidance on image workflows.\n- Improved internal documentation in `README.md` and `SKILL.md` to clarify setup, environment variables, and flow.\n- Modified `clawhub.json` and `run-headless-lookbook.py` to support new verification and lookbook features.\n\nv0.4.0 | 2026-05-06T04:42:27.111Z | auto\n\nVersion 0.4.0\n\n- Updated version number to 0.4.0 throughout the skill.\n- Documentation (`SKILL.md`) and configuration files updated for clarity and maintenance.\n- Internal scripts (e.g., `run-headless-lookbook.py`) adjusted or maintained to align with current workflows.\n\nv0.3.3 | 2026-05-06T04:37:40.560Z | auto\n\n# buck-mason-stylist-skill 0.3.3 Changelog\n\n- Clarified that the skill requires **no environment variables for most workflows**; only the Premium lookbook (AI try-on) needs an optional `OPENAI_API_KEY`.\n- Improved documentation for environment variable setup, making it clear when and why the OpenAI key is required.\n- Made behavior for missing or gated API keys more explicit: the skill clearly falls back to lower-tier lookbooks without blocking flows.\n- Adjusted structure and wording in the environment section for easier setup and understanding by end users.\n- No code or functional changes; this is a documentation and onboarding clarification release.\n\nv0.3.2 | 2026-05-06T04:17:26.413Z | auto\n\n- Clarified that OPENAI_API_KEY is now optional, not required, enabling full functionality for most workflows even if unset.\n- Improved environment variable documentation and downgraded impact for missing API key: premium-tier “AI try-on” requires the key, but standard lookbook and stock check features now work without it.\n- Enhanced fallback behavior: users requesting try-on imagery are given graceful explanations and alternate outputs if the key or access is unavailable.\n- Updated metadata to reflect optional environment variable and adjusted requirement explanations for clarity.\n\nv0.3.1 | 2026-05-06T03:52:16.364Z | auto\n\n### v0.3.1\n\n- Added a dedicated headless lookbook execution script: `scripts/run-headless-lookbook.py`.\n- Updated and clarified references for event suitability, headless mode, and hosting options.\n- Revised documentation in `README.md` and `SKILL.md` with improved separation of runtime vs. repo-only files and clearer agent guidance.\n- Minor adjustments across scripts and metadata to support the new workflow and documentation practices.\n\nv0.3.0 | 2026-05-06T03:28:24.976Z | auto\n\n**Major update with new workflow documentation and supporting scripts/templates.**\n\n- Added detailed workflow references: acceptance checklist, cart rules, event suitability, headless mode, and run layout.\n- Introduced utility scripts for lookbook building, deployment, candidate discovery, event scoring, and validation.\n- Provided new templates including a profile schema for user data consistency.\n- Updated skill documentation to reference new workflows and support materials.\n- Improved clarity for setup, required files, and environment variables.\n\nv0.2.0 | 2026-05-06T02:11:00.146Z | auto\n\nVersion 0.2.0\n\n- Added new reference docs: brand style guide and hosting options.\n- Expanded and clarified the storefront vs. MCP data retrieval workflow, including details on product slug/id mapping.\n- Updated documentation for more precise instructions on required file setup and agent behaviors.\n- Improved references and templates to better support brand voice and multi-workflow shopping flows.\n- Security guidance and metadata were reviewed and updated.\n\nv0.1.7 | 2026-05-04T23:06:25.311Z | user\n\nListing UI fix: revert clawhub.json#env and SKILL.md frontmatter env to flat string arrays so the Runtime requirements panel renders 'OPENAI_API_KEY' instead of '[object Object]'. Move structured metadata (purpose, format, obtain_url, gpt-image-2 verification note, install instructions, publisher) into sibling envDetails / optionalCliDetails maps, keyed by name. Same revert for optional_clis.\n\nv0.1.6 | 2026-05-04T21:47:53.081Z | user\n\nAdd SECURITY.md at repo root: explicit threat model, data flows, opt-in matrix, permission breadth, vulnerability-reporting address. Addresses VirusTotal Code Insight 'suspicious' verdict by documenting what scanners flagged as broad capability surface.\n\nv0.1.5 | 2026-05-04T18:39:13.866Z | user\n\nAddress ClawScan v0.1.4: MPP readback guardrail before link-cli, @stripe verified-publisher pinning, email magic-link reframed as opt-in (default guest order-code, paste-back preferred over agent-reads-mail).\n\nv0.1.4 | 2026-05-01T18:53:33.886Z | user\n\nRicher OPENAI_API_KEY declaration in SKILL.md frontmatter + clawhub.json (purpose, format, obtain_url, gpt-image-2 verification note, how_to_set). New 'Environment' section in SKILL.md and 'Required setup' rewrite in README explicitly explaining the key is only for workflow #3 try-on lookbooks.\n\nv0.1.3 | 2026-05-01T18:29:33.967Z | user\n\nStandardize on gpt-image-2 for image generation. No silent fallback to gpt-image-1.\n\nv0.1.2 | 2026-05-01T18:27:05.316Z | user\n\nDrop 'company key' phrasing throughout; rename pima_company_key -> pima_api_key; cleaner public-facing copy ('Talks to the pima.io MCP'). Source repo at github.com/pima-io/buck-mason-stylist-skill.\n\nv0.1.1 | 2026-05-01T18:14:38.589Z | user\n\nRemove all card-on-file (/api/purchase) references. MPP via stripe/link-cli is the only documented agent-driven payment path. Add github.com/pima-io homepage.\n\nv0.1.0 | 2026-05-01T18:04:42.937Z | auto\n\nInitial release of Buck Mason personal stylist skill.\n\n- Enables personalized shopping for Buck Mason: stock checks (online & in-store), wardrobe gap analysis, outfit and capsule recommendations, and AI try-on lookbooks.\n- Remembers customer sizes and preferences across sessions via `profile.md`.\n- Integrates with Buck Mason’s storefront for discovery and Pima MCP API for structured data, cart building, and checkout.\n- Supports event- and season-aware outfit suggestions using user-supplied events.\n- Allows optional wardrobe seeding and order history via secure account or guest order linking.\n- Requires minimal setup: `profile.md` is mandatory; `wardrobe.md` and `events.md` are optional for enhanced recommendations.\n\nArchive index:\n\nArchive v0.7.2: 48 files, 209872 bytes\n\nFiles: AGENTS.md (4919b), agents/openai.yaml (289b), CLAUDE.md (20000b), clawhub.json (5810b), docs/advanced/pima-api.md (14163b), examples/lookbook.md (8422b), examples/stock-check.md (3663b), PUBLISHING.md (6274b), README.md (9162b), references/acceptance-checklist.md (5150b), references/brand-style.md (16139b), references/event-suitability.md (6625b), references/headless-mode.md (17323b), references/hosting-options.md (22612b), references/image-generation.md (27447b), references/mcp-api.md (14884b), references/mpp.md (22251b), references/output-formats.md (40890b), references/run-layout.md (6359b), references/seasons.md (5105b), references/style-reasoning.md (7425b), references/voting.md (12550b), scripts/build-html-lookbook.py (26652b), scripts/deploy-lookbook.sh (11600b), scripts/discover-weekly-candidates.py (8214b), scripts/inject-voting-ui.py (10725b), scripts/lib/__init__.py (326b), scripts/lib/profile.py (4490b), scripts/run-headless-lookbook.py (34826b), scripts/score-calendar-event.py (6376b), scripts/validate-lookbook.py (10482b), scripts/verify-face.py (8741b), SECURITY.md (11953b), skill-card.md (3490b), SKILL.md (37909b), templates/events.example.md (2070b), templates/profile.example.md (7326b), templates/profile.schema.json (12150b), templates/voting/functions-api-vote.js (2320b), templates/voting/functions-api-votes.js (1672b), templates/wardrobe.example.md (2104b), tests/conftest.py (484b), tests/test_build_html_lookbook.py (6894b), tests/test_profile.py (5784b), tests/test_score_calendar_event.py (4206b), tests/test_validate_lookbook.py (7693b), tests/test_verify_face.py (8932b), _meta.json (143b)\n\nFile v0.7.2:SKILL.md\n\n---\nname: buck-mason-stylist\ndescription: Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lookbooks, and one-shot MPP checkout via link-cli. Customer brings sizes once; the agent reuses them across requests.\nversion: 0.7.2\nlicense: MIT\nauthors:\n  - Buck Mason / Pima\nruntime: any\ncompatibility:\n  mcp_servers: [pima-mcp]\n  binaries: [curl, jq]\nmetadata:\n  openclaw:\n    requires:\n      binaries: [curl, jq]\n      python: [python-pptx, Pillow]\n      optional_env: [OPENAI_API_KEY]\n      optional_binaries: [magick]\n      optional_clis: [\"@stripe/link-cli\"]\n    env_details:\n      OPENAI_API_KEY:\n        required: false\n        conditional: \"Default for any unqualified lookbook request: Premium-tier gpt-image-2 virtual try-on imagery, with Editorial/Minimum used only when Premium prerequisites are missing, fail, or the customer explicitly asks for no AI. Stock checks, recommend, MPP checkout, and order tracking are all unaffected by absence.\"\n        purpose: \"OpenAI /v1/images/edits with model gpt-image-2 for AI try-on imagery.\"\n        format: \"OpenAI API secret key, prefix sk-...\"\n        obtain_url: \"https://platform.openai.com/api-keys\"\n        notes: \"The OpenAI organization must be verified for gpt-image-2 access. Unverified orgs get 403 from /v1/images/edits; the skill surfaces that as an actionable error and offers Editorial tier rather than silently downgrading. See https://help.openai.com/en/articles/10910291.\"\n        how_to_set: \"export OPENAI_API_KEY=sk-...\"\n    optional_cli_details:\n      \"@stripe/link-cli\":\n        purpose: \"Required only for the fully-agent-driven MPP checkout path (workflow #4 step 3). Mints a one-time Stripe Shared Payment Token from the customer's Link wallet.\"\n        install: \"npm i -g @stripe/link-cli\"\n        publisher: \"Stripe (verified npm publisher)\"\n        source: \"https://github.com/stripe/link-cli\"\n        npm: \"https://www.npmjs.com/package/@stripe/link-cli\"\n        notes: \"Install only from the @stripe scope. Pin to a reviewed version in production. The skill never bundles or vendors this CLI.\"\n    categories: [commerce, image-generation, lookbook]\n    tags: [buck-mason, pima, stylist, shopping, stock, mcp]\n---\n\n# Buck Mason personal stylist\n\nYou are acting as a personal shopper for Buck Mason. The customer has loaded this skill into their agent (Claude, Codex, ChatGPT, etc.) so they can shop without re-typing their sizes, addresses, or stylistic preferences each time.\n\n> **What's loaded by agents at runtime, vs repo-only.** Follow refs from this `SKILL.md` only. Files like `README.md`, `PUBLISHING.md`, `SECURITY.md`, and `CLAUDE.md` exist for repo readers, ClawHub reviewers, and skill editors — agents should not load them at runtime. Anything an agent needs is either in the frontmatter, in `references/`, in `templates/`, in `scripts/`, or in `examples/`.\n\n## Environment\n\n**Premium try-on uses one optional environment variable** — every other workflow runs with no env config at all.\n\n| Var | Optional / Required | Used by | How to set |\n|---|---|---|---|\n| `OPENAI_API_KEY` | **Optional** — gates Premium-tier lookbook generation only | Workflow #3 Premium tier (gpt-image-2 try-on imagery via `https://api.openai.com/v1/images/edits`) | `export OPENAI_API_KEY=sk-...` in the shell or secret manager that runs the agent. Get a key at <https://platform.openai.com/api-keys>. |\n\nStock checks, wardrobe gap analysis, recommend, MPP checkout, order tracking, and the **Editorial** + **Minimum** lookbook tiers all work with **no env config**. The Premium tier (gpt-image-2 try-on imagery) is the only thing that needs `OPENAI_API_KEY`.\n\n**Default lookbook behavior:** when the customer says \"lookbook\" without qualifying the format, attempt the Premium virtual try-on path first: gpt-image-2 places the selected clothes on the customer, then the agent assembles and deploys the hosted lookbook with the voting mechanism enabled. Do not choose Editorial tier just because it is faster or cheaper. Use Editorial/Minimum only when Premium prerequisites are unavailable, generation fails, or the customer explicitly asks for a no-AI/read-only artifact.\n\n**`gpt-image-2` access is gated** when you do set the key. The OpenAI organization tied to the key must be **verified for `gpt-image-2`** (see <https://help.openai.com/en/articles/10910291>). Unverified orgs get HTTP 403 from `/v1/images/edits` — surface that as an actionable error and offer Editorial tier rather than silently downgrading to a lesser image model.\n\nIf `OPENAI_API_KEY` is unset or `profile.md` has fewer than two usable reference photos and the customer asks for a lookbook, say that the default virtual try-on needs the missing prerequisite, then offer to continue with Editorial tier. Do not silently produce an Editorial-tier lookbook while calling it the default.\n\n## When to use this skill\n\nActivate when the customer says any of:\n\n- \"Stock check\" / \"do they have ___ in my size\" / \"is ___ available near me\"\n- \"Build me an outfit / lookbook / capsule for [event/trip/season]\"\n- \"What's in my Buck Mason wardrobe\" / \"what gaps do I have\"\n- \"Try this on me\" / \"show me wearing ___\"\n- \"Build me a cart\" / \"check me out\" / \"send me a checkout link\"\n\nIf the request is generic shopping help and Buck Mason isn't named, do **not** activate — defer to a generic shopping skill.\n\n## Required setup (one-time)\n\nThe customer should keep three plain-text files under their agent's persistent memory or workspace:\n\n| File | Purpose | Required? |\n|---|---|---|\n| `profile.md` | sizes per category, fit prefs, color prefs, contact/shipping, home zip, optional reference photo URL | **yes** |\n| `wardrobe.md` | inventory of items they already own (Buck Mason or otherwise) | optional but enables gap analysis |\n| `events.md` | upcoming travel/events with date, location, dress code | optional but enables event-aware suggestions |\n\nTemplates are in `templates/` — `profile.example.md`, `wardrobe.example.md`, `events.example.md`. On first run, if `profile.md` is missing, walk the customer through filling it in. Don't ask for everything at once — start with sizes (shirt, pant waist+inseam, short, jacket, shoe), then home zip, then save and proceed.\n\n**Default to the guest order-code path** (`?order_code=<code>`) for any single-order lookup, return, or tracking request — it sidesteps the email round-trip entirely and never produces a JWT or session token. The email + magic-link flow is **opt-in only** and is only worth the friction if the customer explicitly asks for account-wide history (e.g., seeding their wardrobe from every past order). Even then, prefer asking the customer to paste the magic link back from their own inbox over reading the email programmatically. If the agent does read mail, the operator must explicitly authorize a Gmail/IMAP MCP and the agent must restate that authorization before each retrieval. The full retrieval-method confirmation is in workflow #5.\n\n## Data sources\n\n### Browse the storefront before you query\n\nBefore reaching for the MCP, **read `https://www.buckmason.com` directly** — it's the single best place to absorb the brand vibe, see what's on the homepage right now, find collection narratives (\"Spring '26 Linen Capsule\"), and discover products organically the way a customer would. Use the storefront for:\n\n- **Discovery** — \"What is Buck Mason putting front and center this week?\" → hit `/`, `/collections/men`, `/collections/sale`, the campaign pages linked from the nav. The product `slug`s you'll find there match the MCP `slug` field 1:1, but **don't pass them to `/mcp/buckmason/products/:id`** — that endpoint matches against `code` or numeric `id`, not the lowercase slug. Search by name (`/products?q=…`) to grab the numeric `id`, then look up details by `id`. (Detail in `references/mcp-api.md`.)\n- **Vibe / brand voice** — copy decks, model photography, color palette, formality level. The MCP returns structured data; the storefront tells you how the brand wants you to talk about it.\n- **What the user might want** — when the user is vague (\"something for spring\") or asks \"what would you recommend?\", browse buckmason.com first to see the current season's hero pieces, then drill into the MCP for sizes/stock/imagery on the specific items you want to pitch.\n- **Cross-checking** — confirm pricing, color names, copy descriptions, and whether a product is still live on the site (an MCP record can lag the storefront briefly during a Shopify push).\n\n**Workflow:** browse buckmason.com → land on a candidate set → switch to MCP for structured queries (stock by size + nearby store, full image gallery, capsule recommendations, checkout). Don't skip the storefront step for open-ended requests — the MCP is for *exact* lookups; the storefront is for *finding the question*.\n\n### MCP — structured catalog + transactions\n\nThis skill is built on Pima's `/mcp/*` endpoints — a single, public, agent-friendly surface that returns rich product data, per-store inventory, and MPP checkout challenges. **Read `references/mcp-api.md` for the full contract.**\n\nThe `/api/*` endpoints (documented in `docs/advanced/pima-api.md`) power **orders.buckmason.com** — Buck Mason's live Returns Management and Order Tracking portal — and cover everything the MCP doesn't: customer login, account, order history with shipment + tracking, and return initiation. Reach for them whenever the user asks about an existing order, fulfillment status, or starting a return. **Purchasing happens through the MCP checkout endpoint only**: `POST /mcp/buckmason/checkout` (MPP, agent-driven). The agent does not call any `/api/*` purchase path.\n\n| What you need | Endpoint | Notes |\n|---|---|---|\n| Browse / search catalog (with name, image, price, gender, sizes) | `GET /mcp/buckmason/products` | Filters: `gender`, `category`, `style`, `color`, `q`, `recently_live`, `min_price`, `max_price`, `near_zip`/`radius_mi`. |\n| Single product detail (full image gallery + per-store stock) | `GET /mcp/buckmason/products/:id` | `:id` is `slug`, `code`, or numeric id. |\n| Stock for a specific SKU at nearby stores | `GET /mcp/buckmason/stock/:sku?near_zip=…&radius_mi=25` | Per-location counts, distance, pickup_enabled. |\n| Stores near a zip | `GET /mcp/buckmason/locations?near_zip=…&radius_mi=25` | Pre-sorted by distance. |\n| What's new this season | `GET /mcp/buckmason/seasonal?gender=…` | Recently-live products as season signal until the item-master branch lands. |\n| Taxonomy by gender | `GET /mcp/buckmason/categories?gender=…` | |\n| Capsule recommendation for a context | `GET /mcp/buckmason/recommend?gender=m&occasion=wedding&dress_code=smart_casual&sizes[shirt]=L&sizes[pant]=32x32&sizes[shoe]=10.5&near_zip=…` | Best-effort heuristic. |\n| Fully agent-driven checkout | `POST /mcp/buckmason/checkout` | HTTP 402 challenge → `stripe/link-cli` SPT → charge with `Authorization: Payment <SPT>`. |\n| Customer login & past-order wardrobe seeding | `POST /api/verify_order_or_email` → magic link → `POST /api/login_via_token` → `GET /api/order_history` | **Opt-in only**. Use this *only* when the customer explicitly asks for account-wide history. Prefer the customer pasting the link back; reading mail programmatically requires explicit operator authorization for the email MCP. **Default to `?order_code=` (next row)** for one-off lookups. |\n| **Order tracking + fulfillment status** | `GET /api/order_history?token=<jwt>` (auth) **or** `?order_code=<code>` (guest) | Returns shipments[] with `status`, `tracking_code`, `tracking_url`, `shipped_at`, `estimated_delivery_at`. Same endpoint that powers orders.buckmason.com. |\n| **Initiate / manage a return** | `POST /api/customer_returns` + the return_reasons / shipping_rates helpers in `docs/advanced/pima-api.md` | Powers the Returns Management portal at orders.buckmason.com. |\n| Fully agent-driven checkout (no browser) | `POST /mcp/buckmason/checkout` (MPP) | HTTP 402 challenge → agent mints a Stripe SPT via `stripe/link-cli` (push-approved by the customer in their Link app) → re-POST with `Authorization: Payment <SPT>`. Read `references/mpp.md`. |\n\n**Gender awareness.** Always pass `gender` (`m`/`w`/`u`) on every catalog/recommend call once you've inferred it from the customer's profile. If the customer doesn't specify, ask once and save it to `profile.md`. The default profile template now includes a `gender:` field.\n\n**Seasonality.** Use `GET /mcp/buckmason/seasonal?gender=…` to see what's freshly live on buckmason.com — that's the closest signal to \"what's in season right now\" until the FY26 item-master attributes ship. Combine with the calendar season (`references/seasons.md`) and the customer's region for outfit appropriateness.\n\n**Tenant slug + host.** Every MCP URL is hosted at `https://pima.io/mcp/<company_slug>/...`. For Buck Mason: `https://pima.io/mcp/buckmason/...`. There is no key, header, or cookie required for MCP calls — Buck Mason's public catalog/stock/locations are all open. The `/api/*` flows (login, account, order tracking, returns, checkout) need a customer JWT or guest order_code and are served from the Buck Mason customer host (`https://www.buckmason.com/api/...` and `https://orders.buckmason.com` for the Returns Management and Order Tracking portal). Full reference in `docs/advanced/pima-api.md`.\n\n## Workflows\n\n### 1. Stock check — \"do they have the [item] in my size, online and near me\"\n\n1. **Resolve the item.** Search by name + color + gender:\n   `GET /mcp/buckmason/products?gender=m&q=daily+shirt&color=olive`\n   - **`q` is exact substring (ILIKE %q%) against `name`/`full_name`/`code`/`sku_root`** — not fuzzy. Long phrases must literally appear; prefer short distinctive substrings (`q=daily+shirt`, not `q=daily+shirt+olive+heavy`). Verified 2026-05-09.\n   - **`color` is exact match** including spaces and slashes (`color=Driftwood+Venice+Wash`, not `driftwood-venice-wash`). Pull the canonical color string from a prior products response, don't normalize.\n   - Filter out catalog noise: items with `color: \"vintage_product\"` are Mason Made / archival pieces with a sentinel red `color_rgb: \"#ff0000\"`. Drop them from default browse unless the customer asked for vintage.\n   - If multiple match, present 2–3 with thumbnails (the response includes `image_url`) and ask the customer to pick.\n2. **Pull product detail with stores.** Look up by **numeric `id`** from the search response:\n   `GET /mcp/buckmason/products/<id>?near_zip=<home_zip>&radius_mi=25`\n   - **Don't pass the lowercase `slug`** — `/products/:id` matches against the literal `code` field (which is inconsistently cased across the catalog) or the numeric `id`. Numeric `id` is the only universally-stable form. (Detail in `references/mcp-api.md`.)\n   - The `variants[]` array contains the variant matching the customer's size, with `sku`, `shopify_variant_id`, an `online` object (`{ in_stock, status, label, count? }`), and `locations[]` for per-store stock.\n3. **Match the size.** Pull from `profile.md` based on category (shirt/pant/short/shoe/jacket). Pick the matching `variant.size`. If the size doesn't exist in the profile for that category, ask once.\n4. **Present.** Lead with: \"Online: ✓/✗ (qty). Nearby: list sorted by distance.\" Always include the product URL (from `product.url`). If the customer wants to buy, move to workflow #4 and use MPP checkout after explicit total confirmation.\n\nIf you only have the SKU (not the product), skip steps 1–2 and go straight to `GET /mcp/buckmason/stock/<sku>?near_zip=…&radius_mi=…`.\n\n### 2. Wardrobe gap analysis — \"what am I missing for [season/event]\"\n\n1. Load `wardrobe.md`. If it's thin, offer to seed it from the customer's Pima order history. Account-wide seeding requires the magic-link flow (`POST /api/verify_order_or_email` → the customer clicks the email link OR the agent reads it from a connected inbox tool → `POST /api/login_via_token` → `GET /api/order_history`). **Confirm with the user how the link will be retrieved before sending the email** (workflow #5 step 1c). If the user only wants to seed wardrobe from one or two recent orders, ask for the order numbers and use the `?order_code=` path instead — no email round-trip.\n2. Determine **season + climate + region** from today's date and event context (`references/seasons.md` — note the heat-type column: dry vs humid vs coastal-mild matters for fabric choice). Determine **dress-code tier** (`references/style-reasoning.md` formality scale, 1–6).\n3. Get a season-aware starting point:\n   `GET /mcp/buckmason/seasonal?gender=<m|w>&days=45`\n   This returns recently set-live products grouped by category — but treat it as one *input*, not the answer. \"What's new\" is not the same as \"what's right.\" Cross-reference with classic staples regardless of recency.\n4. Ask `GET /mcp/buckmason/recommend?gender=…&occasion=…&dress_code=…&sizes[shirt]=L&sizes[pant]=32x32&sizes[shoe]=10.5&near_zip=<home_zip>&budget=<from-profile-or-explicit>` for a heuristic capsule. Diff each slot against `wardrobe.md` — keep only the gaps.\n5. **Apply the reasoning filter** (`references/style-reasoning.md`):\n   - Drop picks whose fabric/weight is wrong for the climate (e.g., heavy oxford in humid heat).\n   - Drop picks whose formality tier doesn't match the dress code.\n   - Down-weight picks that conflict with `profile.md → style_ethos`.\n   - Aim for ≥ 60% classic + modern-staple in the final selection; one of-the-moment piece is fine for a one-off event, never the whole look.\n6. For each surviving pick, **write a one-sentence rationale** that touches at least: climate fit, formality fit, and personal/classic angle. \"It's new and in stock\" is not a rationale — if you can't write a real reason, drop the item.\n7. Save the resolved list (with rationale per item) to a session file (`outfit-<date>.md`) so the customer can iterate.\n\n### 3. Lookbook generation — \"lookbook\" / \"show me in these clothes, in [setting]\"\n\nA lookbook is a small set of recommended outfits in a sharable artifact. **Unqualified \"lookbook\" means Premium virtual try-on by default**: use `gpt-image-2` to place the clothes on the customer, assemble a hosted HTML/HTML-cart lookbook, and deploy it with partner/stakeholder voting enabled. Editorial and Minimum tiers are fallback modes, not the default for normal interactive lookbook requests.\n\n**Default tier ladder — attempt Premium first, then fall back only when needed.**\n\n| Tier | Requires | Output |\n|---|---|---|\n| **Premium** | `OPENAI_API_KEY` (gpt-image-2 verified org) + ≥2 reference photos in `profile.md` | AI try-on hero per look |\n| **Editorial** | none | Buck Mason on-model + flat-lay product imagery laid out per look (no AI) |\n| **Minimum** | none | Per-look bullet list with names, prices, clickable URLs, stock lines, one-sentence rationale per pick |\n\nIf Premium prerequisites are missing (`OPENAI_API_KEY`, verified org access, or >=2 usable `profile.md` reference photos), say exactly what is missing and ask whether to continue in Editorial tier. If a Premium generation attempt fails on one look, keep successful Premium looks and use Editorial only for the failed look unless the customer asks to regenerate. The minimum-viable lookbook still includes the things that make the format useful — names, prices, URLs, in-your-size stock, rationale.\n\n**Per-format pick (run regardless of tier):**\n\n| Format | When | Detail |\n|---|---|---|\n| `images` | \"just the photos\" / fastest iteration | Raw `lookbook/<date>-<event>-look-N.png` only |\n| `ppt` | review with stylist / SO | 16:9 `.pptx`, default when MPP isn't reachable |\n| `html` | shareable preview, email body, read-only | Hosted HTML, no buy affordance; still deploy with voting unless the customer asks for read-only |\n| `html-cart` *(default when MPP is reachable)* | customer is going to buy | Hosted interactive HTML with checkbox cart + plain-prose stylist handoff; deploy with voting |\n\n**MPP-reachable** means `@stripe/link-cli` is installed agent-side AND `profile.md → link_payment_method: confirmed` (customer has a Stripe Link wallet with a payment method). When `unconfirmed`, ask once and persist the answer; without it, fall back to `ppt` or `html`. `OPENAI_API_KEY` is a separate gate (try-on images), orthogonal to MPP. If the customer's profile carries `preferred_link_payment_methods` (for example `clothing: \"0896\"`), use it only to choose among live `link-cli payment-methods list` results; never store raw card numbers, CVVs, Stripe SPTs, or Link internal payment-method ids in the profile.\n\n**Build the lookbook**:\n- Premium-tier image-gen: structured prompt template, identity-anchor + build/face fact sheet rules, garment fact sheet per item, setting/composition pulled from `GET /mcp/buckmason/lookbook/settings` — full mechanics in **`references/image-generation.md`**.\n- Output assembly (`images` / `ppt` / `html` / `html-cart` builders, per-format must-haves, brand styling, OG meta tags, responsive breakpoints, lightbox, prose handoff): **`references/output-formats.md`** + **`references/brand-style.md`**.\n- Hosting: **`references/hosting-options.md`** (capability-aware probe + ranked transports).\n- Acceptance gates before sharing the URL: **`references/acceptance-checklist.md`** — every format must clear it.\n- **Partner / stakeholder voting — part of the default lookbook**: every Cloudflare Pages lookbook deploy gets a thumbs up/down vote form per look + per item (free-text comments), stored in a shared Cloudflare KV namespace via a pair of Pages Functions. `scripts/deploy-lookbook.sh` bakes it in automatically when given a KV id (via `--kv-id`, `$LOOKBOOK_VOTES_KV_ID`, or `profile.md → lookbook_votes_kv_id`). If no KV id is available, stop and surface the one-time setup command rather than quietly deploying `--no-voting`. Pass `--no-voting` only when the customer explicitly asks for a read-only static lookbook with no `/api/*` endpoints. The form is unobtrusive when no one votes — but ask the customer once before sharing the URL with a partner, since the partner will see the form. Full architecture, KV setup, schema, and security model in **`references/voting.md`**.\n- Headless / scheduled / cron-mode runs (no questions, defaults assumed, silent unless blocker): **`references/headless-mode.md`**.\n- **Per-lookbook isolation (non-negotiable): `references/run-layout.md`.** Each lookbook gets its own `~/.buck-mason-stylist/runs/<lookbook_id>/` directory; never reuse images, picks, or configs from another run. The build script enforces a `.lookbook_id` marker check and aborts if `--look-images` came from a different lookbook.\n\n**Always disclose** in the cover/footer that try-on images are AI-generated previews, not photos of real garments on the customer.\n\n### 4. MPP checkout — \"buy this for me\" / \"run checkout\"\n\nUse the fully agent-driven MPP path only. **Always read the order total back in plain English before requesting Link approval** — the Link push-approval is the consent step, and there is no second gate after the SPT request goes out.\n\n| Path | When | Contract lives in |\n|---|---|---|\n| **MPP fully-agent-driven** | `profile.md → link_payment_method: confirmed` AND runtime has `@stripe/link-cli` | `references/mpp.md` (two-phase HTTP 402 + Stripe SPT lifecycle, on-paste-back handler from the `html-cart` prose, idempotency, total-mismatch guard, coupon/credit envelope, worked transcript) |\n\nRead `references/mpp.md` before invoking. Don't reconstruct the contract from this paragraph. The `html-cart` lookbook prose-handoff parser, ship/coupon/credit defaults, price-drift check, and post-success wishlist append all live in `references/mpp.md` § \"Entry point — the `html-cart` lookbook prose handoff.\"\n\nFor MPP, `link-cli spend-request create` needs a concrete `--payment-method-id`. Resolve it after phase 1 by running `link-cli payment-methods list`, matching the live cards against `profile.md → preferred_link_payment_methods` for the purchase purpose (`clothing`, `business`, `default`, etc.), and asking the customer to pick if the purpose has no match or multiple live cards share the same last4. Pass the selected id with `--payment-method-id` and `--credential-type shared_payment_token`; do not persist that id after the run.\n\n### 5. Order tracking + returns — \"where's my order\" / \"I want to return this\"\n\nThese are the most common post-purchase questions. They run on the same `/api/*` endpoints that power **orders.buckmason.com** (the Returns Management and Order Tracking portal).\n\n1. **Identify the order.** Three paths, in this preference order — pick the lowest-friction one the user can satisfy:\n\n   **a. Saved JWT** *(zero friction — no user action)*. If `profile.md → jwt` is set from a previous session, just resend it on `Authorization: <jwt>` (raw, no `Bearer` prefix). Skip to step 2.\n\n   **b. Order code** *(lowest friction — recommended default for one-off lookups)*. Ask the user for their order number (e.g., `BM-12345`) — it's at the top of every order-confirmation email and on the printed receipt. Then pass `?order_code=<code>` on every `/api/*` call for the rest of this conversation. **No email read, no magic link, no JWT.** This is the right path for \"where's my order?\" and most return flows.\n\n   **c. Email + magic link** *(high friction — only when the user wants account-wide access, e.g., to see all past orders for wardrobe seeding)*. This is a two-step flow:\n     1. `POST /api/verify_order_or_email` with `{ value: \"<email>\", source: \"returns\" }` — Pima emails a magic link to the customer.\n     2. The customer clicks the link in their inbox, OR the agent reads the email itself and extracts the token, OR the customer pastes the URL/token back into the chat. Then `POST /api/login_via_token` with `{ token: \"<token>\" }` returns a JWT. Save it to `profile.md → jwt` so the next session starts at path (a).\n\n   **CRITICAL — magic-link capability check.** **Prefer \"paste the link back\" over agent-reads-mail.** The magic-link path requires the agent to either:\n   - Have the customer paste the link / token back from their inbox after they receive the email (preferred — no extra capability granted to the agent), OR\n   - Have a tool that reads the customer's email (e.g., a Gmail MCP server with read scope on the inbox of the email used for the purchase) — only with explicit operator authorization for that MCP server, and the agent restates that authorization in the same turn it reads the email, OR\n   - Ask the customer to forward / paste back the link from their inbox (manual relay).\n\n   **Before triggering `/api/verify_order_or_email`, confirm with the user how the link will be retrieved.** Surface the options in plain English: \"I can either read the link from your inbox if you've connected an email tool, or you can paste it back to me after it arrives — which would you prefer?\" Don't silently fire the email and then deadlock waiting for the token.\n\n   **Always prefer (b) when possible.** \"Do you have your order number?\" is a one-second question and avoids both the email round-trip and the email-access permission. Only fall through to (c) when the user explicitly wants account-wide access (e.g., wardrobe seeding from full order history).\n2. **Fetch status.** `GET /api/order_history?token=<jwt>` (or `?order_code=…`) returns the order with a `shipments[]` array — each shipment has `status` (`processing` / `shipped` / `delivered` / `delayed`), `tracking_code`, `tracking_url`, `shipped_at`, and `estimated_delivery_at`. Lead with the soonest estimated delivery + carrier link; mention any in-transit warning.\n3. **Initiate a return.** If the user wants to return an item:\n   - Pull the eligible items from the order (`order.items[].returnable: true` — anything not yet past Buck Mason's return window).\n   - Surface the available `return_reasons` from `GET /api/return_reasons` (plain-text labels like \"Doesn't fit\", \"Wrong color\").\n   - Ask which items + which reason, confirm, then `POST /api/customer_returns` with the chosen items + reason + the return shipping rate from `GET /api/shipping_rates`.\n   - Hand back the return label URL from the response so the customer can print it.\n4. **Surface the portal directly.** For complex multi-item returns or anything the agent can't fully handle, link the customer to **https://orders.buckmason.com/<order_code>** — the same flows as above, but in the customer's browser with full UI.\n\nDon't fabricate tracking numbers or delivery dates from training data. If `/api/order_history` doesn't return what you need, say so and link the customer to orders.buckmason.com.\n\n## Checkout safety\n\nA shopping agent has full read/write access to a checkout flow. Treat any tool call that moves money as a destructive action that needs explicit confirmation in the same turn:\n\n- **Agent-driven (MPP)**: only after the customer has explicitly opted into agent-driven payment, with the total restated in plain English in the same turn. The Stripe Shared Payment Token (minted by `stripe/link-cli` via the customer's push-approval in their Link app) IS the consent. Always echo `acknowledged_total_cents` on phase-2 to catch hallucinated totals, and always pass the live `--payment-method-id` selected from `link-cli payment-methods list`.\n- **Never** save a Stripe SPT, full card number, or CVV to any file. SPTs are one-time-use and short-lived; if you need to retry, mint a fresh one. The agent never asks the customer for raw card data — that flow does not exist in this skill.\n- **Coupon codes**: apply only if the customer named the code or it's already saved as customer credit. Don't go hunting for coupons online — that's a different skill.\n\n## Personalization signals\n\nWhen choosing or recommending items, weight by (in this order):\n\n1. **Hard constraints** — size, dress code (formality tier), season + climate (heat type from `references/seasons.md`).\n2. **Reasoning filter** (`references/style-reasoning.md`) — fabric/weight must match climate (dry vs humid vs coastal mild vs altitude); silhouette must match formality tier; mix at least 60% classic + modern-staple.\n3. **Profile prefs** — favorite colors, avoided fabrics/silhouettes, and especially `style_ethos` (drives the classic-vs-trend balance).\n4. **Wardrobe gaps** — prefer items that fill a gap over duplicating something they own.\n5. **Past orders** — if `account.orders` is loaded, infer fit history (returns suggest a size to avoid; repeat purchases suggest a winning style).\n6. **\"Sold with\"** — Pima exposes `/api/products/:id/sold_with` and `/api/product_lines/:id/sold_with` for cross-sell, useful when filling out a look.\n\n**Every recommendation must carry a rationale.** Output a one-sentence \"why\" per pick that names the climate fit, the formality fit, and the personal/classic angle. The default \"this is in stock and on-trend\" is not acceptable — see `references/style-reasoning.md` for the format and worked example.\n\n## Error handling and degradation\n\n- If Shopify Storefront is unreachable, fall back to Pima endpoints alone — the customer still gets catalog, locations, and checkout, just without rich images and per-store availability. Tell them what's missing and why.\n- If `/api/inventory` returns 403, don't have an inventory token — don't repeatedly retry. Switch to Shopify availability.\n- If a SKU resolves to zero stock everywhere, offer `POST /api/restock_notifications` (`product_code`, `size_name`) and continue with alternatives.\n- If image generation fails or is unavailable in the agent runtime, surface the Premium blocker and offer to continue with Editorial tier. Do not silently convert a requested lookbook into a no-AI artifact.\n\n## Output style\n\n- Lead with the answer, not the methodology. \"Yes, the Daily Shirt in olive, size L is in stock online and at Brentwood (4mi). Pickup today.\" beats a paragraph explaining how you checked.\n- For lists of stores or items, use compact tables.\n- Always include the product page URL alongside any recommendation so the customer can verify.\n- Prices in USD with the dollar sign; convert Pima cents (`9720`) to dollars (`$97.20`) on display.\n\n## Files in this skill\n\n- `SKILL.md` — this file.\n- `references/mcp-api.md` — **primary** API reference: Pima's `/mcp/*` endpoints.\n- `docs/advanced/pima-api.md` — `/api/*` reference (login, account, **order history with shipment + tracking**, **return initiation**). Powers orders.buckmason.com (Returns Management and Order Tracking portal). Used by workflow #5. **Not used for purchasing** — purchases go through `POST /mcp/buckmason/checkout` (MPP).\n- `references/image-generation.md` — OpenAI image API prompt cookbook for try-on + lookbook.\n- `references/seasons.md` — season + region + heat-type mapping for outfit logic.\n- `references/style-reasoning.md` — the *why* engine: climate matrix, formality scale, classic-vs-trend filter, rationale format.\n- `references/output-formats.md` — how to render the lookbook as `images` / `ppt` / `html` / `html-cart`, plus quickest hosting options for the HTML format.\n- `references/brand-style.md` — extracted Buck Mason visual style guide (typography, colors, button shape, image ratios) sourced from buckmason.com directly. **Load before generating any `html-cart`, `html`, `ppt`, or `pdf` lookbook** so the rendered surface reads as Buck Mason, not generic-AI-editorial.\n- `references/hosting-options.md` — capability-aware menu of hosts for the HTML lookbook (Cloudflare Pages → Netlify → Vercel → Surge → Gist → S3 → 0x0.st). Probe script, deploy commands per transport, design rules (confirm before publish, sticky preference in `profile.md`, \"tool installed but unauthenticated\" is a soft-no).\n- `references/mpp.md` — Merchant Payments Protocol (mpp.dev) checkout: HTTP 402 challenge + Stripe Shared Payment Token via stripe/link-cli for fully agent-driven transactions when there's no browser. Two-phase request lifecycle, guardrails, and a worked transcript.\n- `references/acceptance-checklist.md` — the gate every lookbook clears before the agent shares the URL. Operationalized as `scripts/validate-lookbook.py`.\n- `references/headless-mode.md` — five rules for scheduled / cron / voice runs: no questions, defaults, fallback tier, deploy-only-if-pre-authorized, silent-unless-blocker. Run summary format.\n- `references/event-suitability.md` — calendar-driven scoring rubric (0–10) for \"should this event trigger a lookbook?\" with hard-veto for medical/therapy. Operationalized as `scripts/score-calendar-event.py`.\n- `references/run-layout.md` — per-lookbook directory isolation rules + the `.lookbook_id` marker convention. **Hard rule**: never share images/picks/configs across lookbooks. `scripts/build-html-lookbook.py` enforces the marker check.\n- `references/voting.md` — partner / stakeholder feedback capability (thumbs up/down per look + per item, free-text comments, Cloudflare KV + Pages Functions). **On by default in `scripts/deploy-lookbook.sh`**; `--no-voting` opt-out. Describes the KV setup, schema, security model, smoke-test handshake.\n- `templates/profile.example.md`, `wardrobe.example.md`, `events.example.md` — copy these into the customer's workspace and fill in.\n- `templates/profile.schema.json` — JSON Schema for `profile.md`. Validate machine-readably; enum'd allowed values for `gender`, sizes, `link_payment_method`, `preferred_lookbook_host`, etc.\n- `examples/stock-check.md`, `examples/lookbook.md` — concrete walkthroughs of the two main flows.\n- `scripts/build-html-lookbook.py` — deterministic deploy-directory builder (config + picks JSON → index.html + thumbs + og.jpg).\n- `scripts/deploy-lookbook.sh` — Cloudflare Pages deploy wrapper: probes wrangler auth, idempotent project create, local + deployed validate, single-URL output for headless callers.\n- `scripts/validate-lookbook.py` — runs `references/acceptance-checklist.md`. Pass `--dir <local>` and/or `--url <deployed>`.\n- `scripts/score-calendar-event.py` — implements `references/event-suitability.md`. JSON in, `{score, breakdown, action}` out.\n- `scripts/discover-weekly-candidates.py` — surfaces recently-live + previously-unproposed products for the recurring weekly newsletter (`references/headless-mode.md` § \"Recurring weekly newsletter\"). Dedupes against `~/.buck-mason-stylist/wishlist.jsonl`.\n- **`scripts/run-headless-lookbook.py`** — single canonical headless invocation. Composes score (event mode) → discover → curate → build → deploy → validate → wishlist append → run summary. Use `--weekly` or `--event <path>`. Respects the `preferred_lookbook_host_auto` deploy gate. Premium-tier resume builds run the face-verification gate automatically.\n- `scripts/verify-face.py` — face-verification gate for Premium-tier outputs. GPT-4o-vision call + strict rubric (hair / beard / eye color / skin tone / age / asymmetry / off_putting AI-generic look). Exit 0 pass, 1 fail, 2 inconclusive. Spec in `references/image-generation.md` § \"Face verification gate.\"\n- `scripts/inject-voting-ui.py` — post-build injector that adds the thumbs up/down voting widget (per look + per item) + favicon link tags to a built `index.html`. Idempotent. Normally invoked automatically by `scripts/deploy-lookbook.sh` (default-on); run directly for offline iteration on the UI.\n- `templates/voting/functions-api-vote.js`, `functions-api-votes.js`, `wrangler.toml.example` — Cloudflare Pages Functions + binding template for voting. `scripts/deploy-lookbook.sh` copies these into the deploy dir, sed-substitutes project name + lookbook id + KV id, and ships them. Spec: `references/voting.md`.\n\nFile v0.7.2:README.md\n\n# Buck Mason Stylist Skill\n\nA personal-shopping skill for [Buck Mason](https://www.buckmason.com), built for Claude Code, Codex, ChatGPT custom GPTs, and any agent that loads `SKILL.md`–style skills. Talks to the pima.io MCP at `pima.io/mcp/buckmason/*`.\n\n## What it does\n\n- **Stock check** — \"do they have the [item] in my size, online and at the Abbot Kinney store?\" — returns bucketed live counts (`In stock` / `Low stock (N left)` / `Out of stock`) per location.\n- **Wardrobe gap analysis** — \"what am I missing for a Sonoma wedding in May?\" — diffs your owned items against a season + climate + dress-code-aware capsule recommendation, with a one-sentence \"why\" per pick.\n- **AI try-on lookbooks** — by default, \"lookbook\" means gpt-image-2 virtual try-on images of you wearing the recommended outfits, assembled into a hosted HTML/HTML-cart lookbook with partner voting enabled.\n- **One-shot MPP checkout** — `POST /mcp/buckmason/checkout` speaks the [Merchant Payments Protocol](https://mpp.dev): HTTP 402 + Stripe Shared Payment Token via [`stripe/link-cli`](https://github.com/stripe/link-cli), push-approved by the customer in the Link app. See `references/mpp.md`.\n\n## Required setup\n\n### `OPENAI_API_KEY` (for the default AI try-on lookbook workflow)\n\n```bash\nexport OPENAI_API_KEY=sk-...\n```\n\nThe skill posts to `https://api.openai.com/v1/images/edits` with `model: \"gpt-image-2\"` to generate virtual try-on images. **The OpenAI organization tied to the key must be verified for `gpt-image-2`** (see <https://help.openai.com/en/articles/10910291>). Get a key at <https://platform.openai.com/api-keys>.\n\nThe other workflows — stock check, recommend, checkout, order tracking — do **not** require an OpenAI key. They only call the pima.io MCP. If a lookbook request is missing the key or usable reference photos, the skill should say what is missing and offer the Editorial tier instead of silently downgrading.\n\n### Voting on deployed lookbooks\n\nCloudflare Pages deploys include the voting mechanism by default. Create one KV namespace per Cloudflare account, then save the id in `profile.md` as `lookbook_votes_kv_id:` or export it as `LOOKBOOK_VOTES_KV_ID`.\n\n```bash\nCLOUDFLARE_ACCOUNT_ID=<account-id> wrangler kv namespace create LOOKBOOK_VOTES\n```\n\n### Profile\n\nOne profile file in your agent's workspace (copy from `templates/profile.example.md`):\n\n```bash\ncp templates/profile.example.md ~/agent-workspace/profile.md\n$EDITOR ~/agent-workspace/profile.md   # fill in sizes, home zip, build, face, reference photos\n```\n\nThat's it. No Buck Mason credentials. No Pima account.\n\nOptional but useful:\n- `wardrobe.md` — owned items (enables gap analysis)\n- `events.md` — upcoming travel/events (enables event-aware suggestions)\n- `magick`, `python-pptx`, `Pillow` — only needed for the PPT/HTML lookbook output (see `references/output-formats.md`)\n- [`stripe/link-cli`](https://github.com/stripe/link-cli) — required for the MPP fully-agent-driven checkout path\n\nFor MPP checkout, `profile.md` can remember payment preferences by purpose using card last4s, for example `preferred_link_payment_methods: { clothing: \"1234\", business: \"9876\" }`. The agent still resolves the live Link payment-method id with `link-cli payment-methods list` immediately before checkout and passes it to `link-cli spend-request create --payment-method-id`; never store Link internal ids, SPTs, card numbers, or CVVs.\n\n## Codex support\n\n- `AGENTS.md` is the Codex-facing repo guide for editing this skill.\n- `SKILL.md` is the runtime skill entry point used by Claude, Codex, ChatGPT, and other agents.\n- `agents/openai.yaml` provides OpenAI/Codex skill-list metadata and the default `$buck-mason-stylist` invocation prompt.\n- `CLAUDE.md` remains in place for Claude Code-specific maintenance notes.\n\n## Quick start\n\n```text\nYou:  Stock check on the Daily Shirt in olive, in my size near 90291.\nSkill: Online: 1,844 (in stock). Abbot Kinney: 36, Century City: 31. Pickup today.\n       https://www.buckmason.com/products/olive-daily-shirt\n```\n\n```text\nYou:  Build me a 3-look capsule for a Sonoma wedding in May, smart-casual.\nSkill: [pulls /mcp/buckmason/recommend, filters via style-reasoning matrix,\n        diffs against your wardrobe, generates 3 gpt-image-2 virtual try-on\n        images, outputs a hosted voting-enabled HTML lookbook or HTML-cart\n        handoff, then runs a fully-agent-driven MPP checkout if you've opted in]\n```\n\nSee `examples/stock-check.md` and `examples/lookbook.md` for full walkthroughs.\n\n## Files\n\n| File | Purpose |\n|---|---|\n| `AGENTS.md` | Codex-facing repo guidance for editing and validating the skill |\n| `SKILL.md` | Main skill entry point — workflows, data sources, output style |\n| `agents/openai.yaml` | OpenAI/Codex skill UI metadata and default invocation prompt |\n| `references/mcp-api.md` | Pima MCP endpoint contract |\n| `docs/advanced/pima-api.md` | Advanced — legacy `/api/*` reference (login, account, checkout); not needed for v0.1.0 stylist flows |\n| `references/image-generation.md` | OpenAI image-gen prompt cookbook + gpt-image-2 hint inventory |\n| `references/seasons.md` | Season + region + heat-type mapping |\n| `references/style-reasoning.md` | Climate matrix, formality scale, classic-vs-trend filter |\n| `references/output-formats.md` | Lookbook output: `images` / `ppt` / `html` / `html-cart` builders + quickest-host options |\n| `references/brand-style.md` | Buck Mason visual style guide (fonts, colors, button shape, image ratios) extracted from buckmason.com — used by every rendered lookbook builder |\n| `references/hosting-options.md` | Capability-aware menu of hosts for the HTML lookbook — probe script + ranked transports (Cloudflare Pages → Netlify → Vercel → Surge → Gist → S3 → 0x0.st) |\n| `references/voting.md` | Cloudflare Pages voting capability — per-look and per-item thumbs + comments, backed by KV |\n| `references/mpp.md` | Merchant Payments Protocol checkout (mpp.dev + stripe/link-cli) — fully agent-driven transactions via HTTP 402 + Stripe Shared Payment Token |\n| `references/acceptance-checklist.md` | Lookbook validation gates (local + deployed) — implemented by `scripts/validate-lookbook.py` |\n| `references/headless-mode.md` | Cron / scheduled / voice run rules: no questions, defaults, fallback tier, deploy-if-pre-authorized, silent-unless-blocker |\n| `references/event-suitability.md` | Calendar-driven scoring rubric — score events 0–10; ≥6 triggers a lookbook (hard veto for medical/therapy) |\n| `references/run-layout.md` | Per-lookbook directory isolation + `.lookbook_id` marker convention (hard rule against cross-lookbook image reuse) |\n| `templates/*.example.md` | Copy these into your workspace |\n| `templates/profile.schema.json` | JSON Schema for the customer profile — machine-validate enums + required fields |\n| `examples/*.md` | End-to-end walkthroughs |\n| `scripts/build-html-lookbook.py` | Deterministic builder (config + picks → deploy directory) |\n| `scripts/deploy-lookbook.sh` | Cloudflare Pages deploy wrapper with probe + validate gates |\n| `scripts/validate-lookbook.py` | Runs `references/acceptance-checklist.md` against a local dir and/or deployed URL |\n| `scripts/score-calendar-event.py` | Implements `references/event-suitability.md` for calendar-driven invocations |\n| `scripts/discover-weekly-candidates.py` | Surfaces recently-live + previously-unproposed products for the weekly newsletter — dedupes against the long-term wishlist |\n| `scripts/run-headless-lookbook.py` | Canonical end-to-end orchestrator (score → discover → curate → build → deploy → validate → summary) |\n| `scripts/verify-face.py` | Face-verification gate for Premium-tier outputs — GPT-4o-vision rubric against the customer's reference photos |\n| `scripts/inject-voting-ui.py` | Post-build injector for the default Cloudflare Pages voting widget |\n| `templates/voting/*` | Cloudflare Pages Functions and `wrangler.toml` template for lookbook voting |\n| `PUBLISHING.md` | ClawHub distribution path |\n| `SECURITY.md` | Threat model, data flows, opt-in capability matrix, vulnerability reporting |\n\n## Install via ClawHub\n\nListing: <https://clawhub.ai/nickmerwin/buck-mason-stylist-skill>\n\n```bash\nclawhub install nickmerwin/buck-mason-stylist-skill\n\n# Some ClawHub install paths strip the executable bit on unpack — restore it\n# once after installing so the bundled scripts can run directly. (The skill's\n# own command examples all use explicit `bash` / `python3` prefixes that\n# work regardless, so this step is optional but tidier.)\nchmod +x ~/.clawhub/skills/buck-mason-stylist-skill/scripts/*\n```\n\n## License\n\nMIT. See `LICENSE`.\n\n## Contributing\n\nThis skill is Buck Mason / Pima.io–specific by design. The MCP endpoints, brand voice, store footprint, and product taxonomy are all Buck Mason. Forks for other brands are welcome — replace the `/mcp/buckmason/...` paths with your own tenant slug, swap the references, and you're most of the way there.\n\nSource + issues: <https://github.com/pima-io/buck-mason-stylist-skill>\nListing: <https://clawhub.ai/nickmerwin/buck-mason-stylist-skill>\n\nFile v0.7.2:_meta.json\n\n{\n  \"ownerId\": \"kn735q4jn33x2q1w5fpe46kqwd85wzq0\",\n  \"slug\": \"buck-mason-stylist-skill\",\n  \"version\": \"0.7.2\",\n  \"publishedAt\": 1779209488017\n}\n\nFile v0.7.2:references/acceptance-checklist.md\n\n# Lookbook acceptance checklist\n\nThe single source of truth for \"is this lookbook ready to share with the customer.\" Every `html` / `html-cart` / `ppt` lookbook clears these gates **before** the agent emits the URL or filename. The checklist is operationalized as `scripts/validate-lookbook.py` — run it; if it exits non-zero, fix the failure rather than narrating around it.\n\n## Local checks (run against the deploy directory before upload)\n\n| # | Check | Pass criterion |\n|---|---|---|\n| L1 | `index.html` exists | File present, ≥ 4 KB |\n| L2 | All product links absolute + on-brand | Every `<a href>` matching `/products/` resolves to `https://www.buckmason.com/products/<slug>` |\n| L3 | Prices present per piece | At least one `$\\d+` token within each `.piece` block |\n| L4 | Stock lines present per piece | Each `.piece` carries an `In stock` / `Low (N)` / `Out of stock` substring (the bucket strings from `/mcp/buckmason/stock`) |\n| L5 | AI try-on disclosure present (premium tier only) | Page contains \"AI-generated\" or \"AI try-on\" text, in cover or footer — required when any look hero came from gpt-image-2 |\n| L6 | OG meta tags present + absolute | `og:url`, `og:image`, `twitter:image` each resolve to URLs starting with `https://`, **not** containing `{{ABSOLUTE_PAGE_URL}}` / `{{ABSOLUTE_OG_IMAGE_URL}}` placeholders |\n| L7 | `og.jpg` present + correctly sized (multi-file deploys) | File exists in deploy dir, ~1200×630, `.jpg` mimetype, < 500 KB |\n| L8 | Per-piece checkboxes carry the full data set (`html-cart` only) | Every `.piece input[type=\"checkbox\"]` has `data-name`, `data-size`, `data-sku`, `data-qty`, `data-price-cents` |\n| L9 | Subtotals and look totals match piece prices | Per-look total equals the sum of piece prices in that look (rendered via JS only — verify the data attrs sum cleanly) |\n| L10 | No broken inline references | No `data-fullsize=\"\"`, no `<img src=\"\">`, no `href=\"#\"` placeholders |\n\n## Deployed checks (run after `wrangler pages deploy` against the live URL)\n\n| # | Check | Pass criterion |\n|---|---|---|\n| D1 | Page returns HTTP 200 | `curl -sI <page-url>` first line is `HTTP/2 200` (or `HTTP/1.1 200 OK`) |\n| D2 | `og.jpg` returns HTTP 200 + `image/jpeg` | `curl -sI <og-url>` shows `200` + `content-type: image/jpeg` |\n| D3 | Each `look<N>.jpg` returns 200 (multi-file deploys) | One per look section; if any 404s, redeploy with the missing asset |\n| D4 | Meta tags survived the deploy | The page body still contains all 13 OG/Twitter tags (no template substitution dropped them) |\n| D5 | Resolved URLs in OG metadata are reachable | The `og:url` and `og:image` URLs in the served HTML themselves return 200 (catches mismatched-alias bugs where the page deployed but the OG URL points at a stale/wrong subdomain) |\n| D6 | Unfurl preview works | Hit `https://www.opengraph.xyz/url/<encoded-url>` in a browser, or scrape with a `User-Agent: facebookexternalhit/1.1` and confirm the meta tags are visible |\n\n## Warnings (non-blocking, surface to the customer if present)\n\n| # | Warning | When |\n|---|---|---|\n| W1 | Hosted publicly | Any deploy to `*.pages.dev`, `*.netlify.app`, `*.vercel.app`, `*.surge.sh`, `0x0.st`, `gist.github.com` — surface \"anyone with this link can view\" before sending |\n| W2 | Look hero is on-model, not AI try-on | When the lookbook degraded to editorial tier — say so in the agent's reply (\"here's the editorial fallback — set `OPENAI_API_KEY` for AI try-on\") |\n| W3 | One or more pieces are out of stock | When any piece's stock label is `Out of stock` — explicit one-line note (\"the X is currently out — I left it in the lookbook for context but you can't add it to a cart\") |\n| W4 | Low stock on any piece | When any piece is `Low (N)` — surface the count so the customer can decide |\n\n## Failure handling\n\nA failed local check **blocks the deploy**. The agent fixes the artifact and retries — never deploys a broken lookbook and tells the customer to ignore the broken parts.\n\nA failed deployed check **blocks the share**. The agent either redeploys (if the failure is recoverable, e.g. og.jpg upload failed mid-flight) or surfaces the failure to the customer and offers the local file as a fallback.\n\nThe validate script returns one of:\n\n- `0` — all checks pass; safe to share\n- `1` — one or more local checks failed (block deploy)\n- `2` — one or more deployed checks failed (block share)\n\nWarnings (`W*`) never affect the exit code; they print to stderr with a `WARN:` prefix.\n\n## Composition with other docs\n\n- **Per-format must-haves** in `references/output-formats.md` define WHAT each piece needs (price, stock, link, total). This checklist is HOW we verify they made it through.\n- **Hosting flow** in `references/hosting-options.md` describes the deploy steps. Run this checklist against the local artifact pre-deploy and against the URL post-deploy.\n- **Brand style** in `references/brand-style.md` is the visual contract — it's not in this checklist (style drift is harder to assert programmatically than presence-of-link), but a layout regression that breaks the responsive grid would surface as a structural failure here.\n\nFile v0.7.2:references/brand-style.md\n\n# Buck Mason brand style guide\n\nA snapshot of the live storefront's visual language, extracted from `buckmason.com` directly (homepage + `/collections/curved-hem-tees` + `/products/white-slub-curved-hem-tee`) on **2026-05-05**. Use this when rendering any branded surface inside the skill — the `html-cart` lookbook in particular — so the output reads as \"by Buck Mason,\" not \"AI-generated for Buck Mason.\"\n\nFor higher-stakes artifacts the agent can optionally re-extract live (script in the appendix). Treat this file as a fast default; trust the live site if they diverge.\n\n## At a glance\n\n- **One typeface family does almost everything**: Adobe **Acumin Pro** (regular + condensed). Body text is regular Acumin Pro; every label, headline, button, and nav item is **Acumin Pro Condensed, UPPERCASE, with +2% letter-spacing**. There are no serifs anywhere on the standard catalog. Don't reach for Georgia, Canela, Söhne, or any \"editorial serif\" instinct — that's not how Buck Mason looks.\n- **The page is white.** `#FFFFFF` body, `#333333` text. Off-white `#F3F1EF` shows up only as an accent (header announcement banner). Don't paint the whole page off-white; that reads more J.Crew than Buck Mason.\n- **Imagery does the heavy lifting.** Headlines are small (~20px), tightly scaled, never bigger than the body's gallery photos. The brand voice is conveyed by hero photography and copy decks, not by display typography.\n- **Sharp corners.** `border-radius: 1px` (≈ 0) on CTAs, size selectors, and product tiles. Pills only on round badges (color swatches, etc., where `border-radius: 50%`).\n- **3:4 portrait product crops** (`0.75` aspect). Not 4:5, not 1:1, not 16:9.\n\n## Colors\n\n| Role | Value | Where it shows up |\n|---|---|---|\n| Page background | `#FFFFFF` (rgb 255,255,255) | `<html>`, `<body>`, `<main>` |\n| Primary text | `#333333` (rgb 51,51,51) | Body, headings, prices |\n| Active CTA | `#000000` on white text | \"Add to Bag\" once a size is selected (inferred from disabled state) |\n| Disabled CTA | `#AAAAAA` background, white text | \"Select Size to Add to Cart\" pre-size-pick |\n| Accent neutral | `#F3F1EF` (warm off-white) | Header announcement banner; subtle section panels |\n| Disabled-element text | `#D9D9D9` | Sold-out size selectors |\n| Border / hairline (when used) | `rgba(0,0,0,0.08)`-ish | Product tiles, form fields |\n\nThe site is otherwise **chromatic only through product imagery** — no brand reds/greens/blues. Stick to the white + black + grayscale stack and let the photos carry color.\n\n## Typography\n\n### Stacks\n\n```css\n/* Body */\nfont-family: acumin-pro, Helvetica, sans-serif;\n\n/* Headlines, labels, nav, buttons — every cap/eyebrow/CTA */\nfont-family: acumin-pro-condensed, Helvetica, sans-serif;\n```\n\nAcumin Pro / Acumin Pro Condensed are licensed via Adobe Fonts. If the rendered surface is a self-contained file (`html-cart`) and we deliberately don't `<link>` external font CDNs (per `references/output-formats.md`), fall back to **`Helvetica Neue Condensed`** then **`Helvetica`** for the condensed face, and **`Helvetica`** then a generic sans for the regular face. Don't substitute Inter, Söhne, IBM Plex, or any other variable sans — they'll read as \"designer-default,\" not Buck Mason.\n\n```css\n/* Acceptable degraded stack for self-contained HTML */\n--bm-cond: \"Acumin Pro Condensed\", \"Helvetica Neue Condensed\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n--bm-body: \"Acumin Pro\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n```\n\n### Sizes (live values, computed)\n\n| Element | Family | Weight | Size | Line-height | Letter-spacing | Transform |\n|---|---|---|---|---|---|---|\n| Body | acumin-pro | 400 | 13px | 18.2px (1.4) | normal | none |\n| Nav link | acumin-pro-condensed | 400 | 16.4px | — | 0.33px (≈2%) | UPPERCASE |\n| Hero CTA label | acumin-pro-condensed | 600 | 18.2px | — | 0.36px (≈2%) | UPPERCASE |\n| PLP h1 (collection name) | acumin-pro-condensed | 400 | 20.15px | 20.15px (1.0) | 0.40px (≈2%) | UPPERCASE |\n| PLP h2 (collection blurb) | acumin-pro-condensed | 700 | 19.5px | 27.3px | 0.39px (≈2%) | UPPERCASE |\n| PDP h1 (product title) | acumin-pro-condensed | 600 | 20px | 20px (1.0) | 0.40px (≈2%) | UPPERCASE |\n| PDP price | acumin-pro | 400 | 14px | — | normal | none |\n| Add-to-Bag (disabled) | acumin-pro-condensed | 700 | 14.3px | — | 0.29px (≈2%) | UPPERCASE |\n| Sold-out size button | sans-serif | 400 | 11.7px | — | normal | none |\n\n### The 2% letter-spacing rule\n\nAlmost every condensed-uppercase element on the site has letter-spacing of approximately `0.02em` (= 2% of font size). Apply this to every uppercase label, eyebrow, button, and nav item. The exact value matters: `0.18em` is too loose (reads as \"design-systemy,\" not Buck Mason), `0` is too tight (reads as a tee-shirt logo).\n\n```css\n.bm-label { font-family: var(--bm-cond); text-transform: uppercase; letter-spacing: 0.02em; }\n```\n\n### Headlines stay small\n\nThis is counterintuitive coming from most agencies' \"editorial\" defaults. Buck Mason's largest h1 on a typical page is **20px**. The brand's display moment is the photography, not 72px serif type. Don't render a `<h1>` at 48px even on a \"cover\" — keep the title compact and let the lookbook image tile carry the space.\n\n### Custom fonts present but unused on standard catalog\n\nThe site loads `@font-face` rules for `MasonGothic` (300/400/700) and `PonomarUnicode`. Neither was in use on the homepage, mens collection, or a representative PDP at extraction time. They likely render on marketing/editorial pages or the Mason Made sub-brand. **Don't substitute either into our HTML output** — match the live storefront's standard-catalog look, which is Acumin-only.\n\n## Buttons & form controls\n\n### Primary CTA (\"Add to Bag\", \"Send to my stylist\", etc.)\n\n```css\n.bm-cta {\n  font-family: var(--bm-cond);\n  font-weight: 700;\n  font-size: 14px;            /* 14–18px range; 14 for inline, 18 for hero overlays */\n  text-transform: uppercase;\n  letter-spacing: 0.02em;\n  color: #ffffff;\n  background: #000000;        /* solid black when active */\n  border: 0;\n  border-radius: 1px;          /* essentially square; never 4px+ pills */\n  padding: 16px 22px;\n  width: 100%;                 /* full-width on PDP-style pages */\n  cursor: pointer;\n}\n.bm-cta[disabled] { background: #aaaaaa; cursor: not-allowed; }\n```\n\n### Secondary / outlined CTA\n\nPattern observed: black 1px border, transparent or white background, black text, otherwise identical typography. Use for \"Cancel\" / \"Back\" / lower-priority actions.\n\n```css\n.bm-cta-secondary {\n  border: 1px solid #1a1a1a;\n  background: transparent;\n  color: #1a1a1a;\n  /* same typography as .bm-cta */\n}\n```\n\n### Size selector (rectangular, NOT pill)\n\n```css\n.bm-size {\n  width: 58px; height: 41px;   /* slightly wider than tall */\n  border: 1px solid #1a1a1a;\n  background: #ffffff;\n  font-family: sans-serif;     /* Buck Mason uses generic sans here, deliberately plain */\n  font-size: 12px;\n  border-radius: 1px;\n  cursor: pointer;\n}\n.bm-size[aria-selected=\"true\"] { background: #1a1a1a; color: #ffffff; }\n.bm-size[aria-disabled=\"true\"] { color: #d9d9d9; cursor: not-allowed; }\n```\n\n### Checkbox (interactive lookbook handoff)\n\nThe storefront doesn't expose a custom checkbox we can mirror directly, but the same minimal black/white logic applies: 16×16px, 1px black border, black fill on `:checked`, no rounded corners. Already in the `html-cart` skeleton in `references/output-formats.md`.\n\n## Images\n\n- **Aspect ratio for product imagery: 3:4 portrait** (`width / height = 0.75`). Native files are 1350×1800; displayed around 900×1200. Use this ratio for both gallery hero images and lookbook try-on images. **Do not use 4:5, 1:1, or 16:9** — they read as Instagram, not Buck Mason.\n- **No filter, no overlay, no faux-vintage.** Photography is direct, well-lit, naturally toned. Try-on images generated by gpt-image-2 should be passed through with no post-effect.\n- **Hover swap** is the standard product-tile interaction (front view → back/detail view). Optional in the lookbook; don't bother for v1.\n- **CDN host is `cdn.shopify.com`** — already declared in `clawhub.json#permissions.network`.\n\n### Social-preview hero (`og.jpg`)\n\nDifferent format, same brand rules. When the lookbook is hosted (`html`/`html-cart`), the agent ships a **1200×630 JPEG** alongside the page so iMessage / Slack / Discord / Twitter unfurl with a hero tile. Two rules:\n\n- **Letterbox the 3:4 hero on a white (`#FFFFFF`) background.** Don't crop the model out of frame to fit 1.91:1 — crop loses the outfit; letterbox preserves it. Buck Mason's white page background absorbs the letterbox naturally.\n- **No type overlay on the OG image.** The chat client renders the page title separately; doubling up reads as cluttered. The image itself is just the look.\n\nPillow recipe lives in `references/hosting-options.md` § \"The og.jpg artifact.\" Shopify-CDN fallback for when local generation isn't possible is in the same section.\n\n## Layout\n\n- **Generous whitespace.** Section padding is large; 48–96px vertical between page sections is typical.\n- **No visible dividers.** No `<hr>` elements rendered on the PDP. Sections separate via whitespace + image/text alternation. **Don't add hairline rules** to the lookbook between sections — a 64px gap and a section-eyebrow label does the job.\n- **Eyebrow labels** carry section identity instead of dividers: small uppercase Acumin Pro Condensed in `#666` or `#888`, ~11px, +2% letter-spacing. (\"LOOK 01\", \"ABOUT THE PIECE\", \"STORES NEAR YOU\".)\n- **Mobile-first column collapse.** Two-column product layouts collapse to single-column under ~700px. Already in the `html-cart` skeleton.\n\n## Voice / copy hints (not visual, but useful when generating labels)\n\n- Sentence-case and Title Case both appear; UPPERCASE for labels/buttons/nav only.\n- Product titles are descriptive and lowercase-friendly: \"Slub Curved Hem Tee,\" \"White Field Spec Boyfriend Crop Tee.\" Use the exact `product.name` from the MCP — don't embellish.\n- Section eyebrows are short and direct: \"ABOUT THE COLLECTION,\" \"TEES MADE HERE,\" \"STORES NEAR YOU.\" Two-to-four words, uppercase.\n- Avoid AI-tropey phrases: \"Discover…\", \"Step into…\", \"Embrace…\", \"Curated for you,\" \"Your perfect…\". They read as catalog-template; the real brand voice is plainer.\n\n## What NOT to do\n\n- ❌ Serif headlines (Georgia, Canela, Playfair, Söhne, etc.). Brand is condensed sans only.\n- ❌ Off-white page backgrounds (`#FAF8F4`, `#F5F1EA`). White only. Off-white is a banner accent.\n- ❌ Pill buttons (`border-radius: 999px` or even `8px`). Sharp corners only.\n- ❌ Heavy section dividers, decorative rules, or borders around content blocks.\n- ❌ Display-size headlines (40px+). Buck Mason's biggest h1 is 20px; let the photography fill the space.\n- ❌ Square (1:1) or landscape (4:5, 16:9) product crops. 3:4 portrait.\n- ❌ External font `<link>` tags or webfont CDN URLs in self-contained outputs (`html-cart` must work offline).\n- ❌ Color accents from outside the product photography (no brand red/blue/green).\n- ❌ \"Curated for you\" / \"Discover your style\" copy patterns. Plain noun-phrase eyebrows only.\n\n## Re-verifying against the live site\n\nWhen rendering a high-stakes artifact, the agent can re-extract these facts in ~5 seconds. The single-batch script (Chrome MCP):\n\n```\n1. Open a tab on https://www.buckmason.com/products/white-slub-curved-hem-tee\n2. Run this JS:\n\nconst s = (el, props) => { if (!el) return null; const cs = getComputedStyle(el); const o = {}; props.forEach(p => o[p] = cs.getPropertyValue(p).trim()); return o; };\nconst out = {};\nout.body = s(document.body, ['font-family','font-size','color','background-color']);\nout.h1   = s(document.querySelector('h1'), ['font-family','font-weight','font-size','letter-spacing','text-transform']);\nout.cta  = s(document.querySelector('button[class*=cta_btn]'), ['font-family','font-weight','font-size','letter-spacing','color','background-color','border-radius']);\nconst img = document.querySelector('img[src*=\"cdn.shopify\"]');\nout.imgRatio = img ? (img.naturalWidth / img.naturalHeight).toFixed(3) : null;\nJSON.stringify(out, null, 2);\n```\n\nIf any of these drift materially from the values in this doc (e.g., body font becomes \"Söhne\", aspect ratio becomes 0.8), update this file in the same commit + bump the lockstep version (CLAUDE.md § Versioning).\n\n## Applying the brand style across formats\n\nThe rules above are written CSS-first because that's the easiest target, but they apply to **every rendered branded artifact** the skill produces — `html`, `html-cart`, `ppt`, and any future `pdf` output. Below is how each rule translates per builder.\n\n### `ppt` (`python-pptx`)\n\n| Brand rule | python-pptx mapping |\n|---|---|\n| Body font | `run.font.name = \"Acumin Pro\"` (fallback \"Helvetica Neue\") |\n| Display / labels | `run.font.name = \"Acumin Pro Condensed\"` (fallback \"Helvetica Neue Condensed\") |\n| Uppercase | Write the string already in uppercase — pptx has no `text-transform` |\n| +2% letter-spacing | `run.font.spc` is the OOXML spelling; many readers ignore it. Skip if it doesn't survive a save-and-reload — the typeface choice carries 90% of the brand feel |\n| 600/700 weight | `run.font.bold = True` |\n| Slide background `#FFFFFF` | `slide.background.fill.solid()` → `fill.fore_color.rgb = RGBColor(0xFF, 0xFF, 0xFF)` |\n| Text color `#333333` | `run.font.color.rgb = RGBColor(0x33, 0x33, 0x33)` |\n| Headline size ~20pt | `run.font.size = Pt(20)` for the slide title; **don't go to 36pt+ \"presentation titles\"** even though that's pptx's default — Buck Mason headlines stay small |\n| Eyebrow ~11pt | `Pt(11)`, condensed, `RGBColor(0x66, 0x66, 0x66)` |\n| 3:4 portrait hero | Place at `width=Inches(4.5), height=Inches(6.0)` (or any same-ratio pair). **Don't use 16:9 landscape for the hero** even though the slide itself is 16:9 — the photo stays portrait and lives on the left half |\n| Sharp corners | `python-pptx`'s default `add_picture` has no rounded corners — leave defaults. Don't apply preset rounded shapes (`MSO_SHAPE.ROUNDED_RECTANGLE`) to image frames |\n| No section dividers | Don't draw lines between content blocks; use blank space and a small uppercase eyebrow label instead |\n\nAcumin Pro / Acumin Pro Condensed aren't bundled with Office or macOS; recipients without an Adobe Fonts subscription will see the system fallback. That's an acceptable degradation — the layout, color, and tone still read as Buck Mason. Don't try to embed the TTFs (license issue, file bloat). The fallback chain in the table above lands on Helvetica Neue, which is already on macOS and Office bundles.\n\n### `pdf` (when it lands)\n\nThere's no `pdf` builder in the skill today; the cleanest implementation when it ships is to **render the existing `html` / `html-cart` template through `wkhtmltopdf` or headless Chromium** (`weasyprint`, Playwright `page.pdf()`). In that case the brand style flows through automatically — same CSS, same fonts, same crops. Don't reach for `reportlab` or `fpdf2` and re-implement; you'll fight the typography for a week and still come out worse than rasterizing the HTML.\n\nIf `OPENAI_API_KEY` is unset and the agent falls back to a flat-lay PDF (no try-on imagery), keep the brand rules: white bg, condensed sans labels, 3:4 product crops, no decorative borders.\n\n### `html` and `html-cart`\n\nThe CSS skeleton in `references/output-formats.md` § 3 and § 4 already encodes the brand rules — fonts, color, type scale, button shape, image ratios. If you edit those skeletons, cross-check this doc.\n\n### `images` (raw PNGs)\n\nSkip — there's no assembly step, just the OpenAI output files. The image generation prompt itself follows different rules (see `references/image-generation.md`); brand-style.md doesn't apply.\n\n## When to load this doc\n\n- **Always**, before generating any `html-cart`, `html`, `ppt`, or `pdf` lookbook — anything visible and branded.\n- **Skip** for non-rendered outputs: text-only chat replies, JSON handoff blocks, raw `images` lookbooks. The brand style is for assembled visible artifacts only.\n\nFile v0.7.2:references/event-suitability.md\n\n# Event suitability rubric\n\nFor calendar-driven invocations (the agent reads a customer's calendar and decides whether to proactively build a lookbook for an event), score the event 0–10 on \"would this customer benefit from a wardrobe-aware lookbook?\" Only events scoring ≥ **6** trigger a lookbook generation; everything below scores doesn't.\n\nThe agent never generates a lookbook for events it shouldn't (medical appointments, work syncs, errands) — that's how trust is kept on the calendar-integration channel.\n\n## The rubric (0–10)\n\n```\nscore = type_weight + dress_code_weight + duration_weight + location_weight + customer_signal\n```\n\n### type_weight (0–4)\n\n| Event class | Weight | Examples |\n|---|---|---|\n| Wedding / engagement / formal | 4 | \"Sarah & Tom's wedding\", \"Black-tie gala\", \"Bar mitzvah\" |\n| Travel (multi-day) | 3 | \"SF trip Mar 14–18\", \"Paris vacation\", \"Sonoma weekend\" |\n| Concert / show / nightlife | 2 | \"Tycho at Greek\", \"dinner + show\", \"speakeasy\" |\n| Party / dinner / social | 2 | \"Friend's birthday\", \"Friday dinner with [name]\" |\n| Conference / professional headshot / on-stage | 2 | \"Stripe Sessions\", \"podcast taping\", \"panel\" |\n| Meeting / one-on-one / casual lunch | 0 | \"1:1 with Alex\", \"coffee w/ J\" |\n| Errand / appointment / admin | 0 | \"Costco run\", \"DMV\", \"dry cleaner\" |\n| Medical / dental / therapy / mental health | -10 | \"Dr. Lee\", \"annual physical\", \"therapy\" |\n| Childcare / family logistics | 0 | \"School pickup\", \"Soccer practice\" |\n| Reminder / TODO / no-context block | 0 | \"Block: focus\", \"Reminder: pay bills\" |\n\nA `-10` weight is a hard veto — the agent never builds a lookbook for medical or therapy appointments, regardless of other signals. This is one of the trust-preserving rules; getting this wrong feels invasive.\n\n### dress_code_weight (0–3)\n\nInferred from the event description if explicit, otherwise from the type:\n\n| Cue | Weight |\n|---|---|\n| Explicit dress code in the event (\"smart casual\", \"black tie\", \"creative\", \"festive attire\") | +3 |\n| Venue implies a code (\"dinner at Bestia\", \"speakeasy\", \"the Standard rooftop\") | +2 |\n| Outdoor / vacation / travel where wardrobe matters | +2 |\n| No dress-code cue, casual default | 0 |\n\n### duration_weight (0–1)\n\n| Duration | Weight |\n|---|---|\n| Multi-day (2+ nights) | +1 |\n| Single-evening / single-day | 0 |\n\nMulti-day trips need a capsule, not a single outfit — that earns the lookbook a small bonus regardless of formality.\n\n### location_weight (0–1)\n\n| Location | Weight |\n|---|---|\n| Travel destination ≠ customer's home metro | +1 |\n| Local | 0 |\n\nDifferent climate / dress norms = harder to plan → lookbook value-add.\n\n### customer_signal (-2 to +2)\n\nReads the customer's chat history (or `profile.md → notes`) for explicit ask:\n\n| Signal | Weight |\n|---|---|\n| Customer asked to be reminded (\"set me up for Sonoma weekend\") | +2 |\n| Customer mentioned the event positively to the agent before | +1 |\n| Customer told the agent to skip lookbook prompts on this event class | -2 |\n\n## Score → action\n\n| Score | Action |\n|---|---|\n| ≤ 5 | Skip silently. Don't surface anything. |\n| 6 | Soft surface — one sentence on the next interactive turn (\"you have X coming up — want a lookbook?\"). Never auto-generate. |\n| 7–8 | Auto-generate the **Editorial tier** **locally** (no try-on; product imagery + flat-lays). Saves the OpenAI cost; customer can request the premium tier if they want try-on. |\n| 9–10 | Auto-generate the **Premium tier** **locally**. The customer probably wants try-on imagery for an event scoring this high. |\n\n**Generate ≠ deploy.** \"Auto-generate\" here means the agent runs the score → curate → build chain locally and writes the lookbook to `~/.buck-mason-stylist/runs/<lookbook_id>/deploy/`. It does **not** mean the agent deploys the URL publicly — that step still requires `profile.md → preferred_lookbook_host_auto: true` per `references/headless-mode.md`. Without `_auto: true`, an auto-generated lookbook lands locally with a summary saying \"ready to deploy\"; the customer publishes interactively when ready.\n\nAuto-generation runs in headless mode (`references/headless-mode.md`) — silent unless there's a result or blocker. The customer receives the run summary on their notification channel.\n\n## Worked examples\n\n| Event | type | dress | duration | location | signal | total | action |\n|---|---|---|---|---|---|---|---|\n| \"Sarah & Tom's wedding · Sonoma · May 9–11 · smart casual\" | 4 | 3 | 1 | 1 | 0 | 9 | Premium auto-generate |\n| \"Stripe Sessions · SF · Apr 26–28\" | 2 | 0 | 1 | 1 | 0 | 4 | Skip (under 6) — but the customer's prior interaction asked for it, so customer_signal = +2 → 6 → soft surface |\n| \"Friday dinner — Bestia\" | 2 | 2 | 0 | 0 | 0 | 4 | Skip silently |\n| \"Friday dinner — Bestia (smart casual)\" | 2 | 3 | 0 | 0 | 0 | 5 | Skip silently |\n| \"Paris vacation · Jun 12–18\" | 3 | 2 | 1 | 1 | 0 | 7 | Editorial auto-generate |\n| \"Annual physical with Dr. Lee\" | -10 | 0 | 0 | 0 | 0 | -10 | Hard skip |\n| \"1:1 with Jamie\" | 0 | 0 | 0 | 0 | 0 | 0 | Skip silently |\n| \"Block: focus time\" | 0 | 0 | 0 | 0 | 0 | 0 | Skip silently |\n| \"Tycho · Greek Theater · Aug 14\" | 2 | 2 | 0 | 0 | 0 | 4 | Skip silently — but a customer who's asked the agent to flag concerts before would push to 5–6 |\n\n## Why this is conservative\n\nThe agent's value in calendar-driven mode is \"I noticed something useful you might miss\" — that requires very high precision. False positives (\"you have a dentist appt coming up, want a lookbook?\") destroy trust faster than false negatives (\"the agent didn't flag the wedding\"). The rubric weights are tuned so that ≥6 fires only on events where most customers would say \"yes, that's a real wardrobe moment.\"\n\n## How to implement\n\nThe scoring lives in `scripts/score-calendar-event.py`. Pass an event object (title, description, dress code, duration, location, customer-history snippets) on stdin or as JSON; get back `{ score, breakdown, action }`. The script is deterministic and side-effect-free — agents call it many times per calendar pass without billing or rate-limit concerns.\n\n## Composition with other docs\n\n- The triggered run uses `references/headless-mode.md` to actually generate the lookbook. The score determines *whether* to fire; headless mode determines *how*.\n- The customer can disable calendar-driven scoring entirely by setting `profile.md → calendar_scoring: off`. Default is `on` once the customer has connected a calendar source — but **never auto-enabled without an explicit calendar connection**, which is itself an opt-in.\n\nFile v0.7.2:references/headless-mode.md\n\n# Headless / cron mode\n\nHow the skill behaves when no human is at the keyboard — scheduled `/loop` runs, calendar-triggered automations, OpenClaw routines, voice-only contexts where there's no chance to interject. The mode doesn't add new workflows; it constrains the existing ones to make autonomous execution safe and silent unless something blocks.\n\n## When this mode applies\n\nThe agent operates in headless mode when **any** of these is true:\n\n- `profile.md → preferred_lookbook_host_auto: true` (explicit opt-in)\n- The invocation came from a recurring schedule (`/loop`, cron, OpenClaw routine), AND a prior interactive session opted into recurring deploys\n- The invocation context has no chat surface (voice agent doing post-event playback; ambient assistant)\n\nIf none of these is set, the agent stays in interactive mode — that's the default. **Headless mode is opt-in**, never inferred from \"the user seems busy.\"\n\n## The five rules\n\n### 1. No questions\n\nThe agent doesn't ask anything. If a decision needs to be made:\n\n- Take the documented default (see § \"Defaults\" below).\n- If no default exists for the decision and skipping it would block the run, **fail loudly to stderr / blocker channel** and stop. Don't guess.\n\nA scheduled run that opens a clarification question is a bug — it just sits there waiting.\n\n### 2. Use defaults\n\n| Decision | Default in headless mode |\n|---|---|\n| Lookbook format | `html-cart` if MPP-reachable, else `html` (read-only); **never** `ppt` (no review surface) |\n| Number of looks | 2–3 (skip the optional 4th–5th) |\n| Output tier | One-shot/event lookbooks: Premium if `OPENAI_API_KEY` + ≥2 reference photos; otherwise Editorial, then Minimum. Recurring weekly newsletters: Editorial unless `weekly_lookbook_tier: premium` |\n| Hosting transport | `profile.md → preferred_lookbook_host`, else the highest-ranked transport from the `references/hosting-options.md` probe |\n| Hosting confirmation | **Skip** when `preferred_lookbook_host_auto: true`; otherwise abort the run with `BLOCKER: hosting needs interactive confirm` |\n| Voting | On for Cloudflare Pages deploys. Resolve `lookbook_votes_kv_id` from `profile.md` or `$LOOKBOOK_VOTES_KV_ID`; missing id is a blocker unless the run was explicitly requested as read-only / `--no-voting` |\n| Coupon / credit | Apply available customer credit (default in workflow #4); no coupon hunting |\n| Pickup vs ship | Ship to `profile.md → shipping_address` |\n| Out-of-stock pieces | Drop from the lookbook (note the drop in the run summary), don't substitute |\n\n### 3. Premium try-on first for one-shot / event lookbooks\n\nThe normal one-shot or event-triggered lookbook default is Premium: gpt-image-2 try-on imagery placed on the customer. The Editorial tier (product imagery + flat-lays, no gpt-image-2 call) is the documented fallback when:\n\n- `OPENAI_API_KEY` is unset, or\n- Fewer than 2 `reference_photos` resolve, or\n- gpt-image-2 returns a non-success status (rate limit, billing block, content-policy refusal — surface the reason in the run summary).\n\nThe minimum-viable-lookbook tier (text + links + stock + rationale) is the deeper fallback when even product imagery isn't reachable (e.g., MCP unreachable). Always produce *something*, but report the fallback reason in the run summary.\n\n### 4. Deploy only if explicitly pre-authorized\n\nHosting an artifact publicly is a one-way action and the existing skill rule is \"always confirm before publishing.\" Headless mode preserves the safety by requiring **explicit pre-authorization**:\n\n- `profile.md → preferred_lookbook_host_auto: true` is the **only** way the agent skips the per-publish confirm.\n- This flag should only be set after the customer has manually deployed at least once on the chosen transport (so they've seen the URL pattern + retention model).\n- Even with `_auto: true`, the agent surfaces *what was deployed and where* in the run summary — silent success is allowed; silent failure is not.\n\nIf `_auto: true` is not set, headless mode produces the lookbook locally (under `~/.buck-mason-stylist/runs/<lookbook_id>/`) and emits a one-line run summary saying \"ready to deploy — set `preferred_lookbook_host_auto: true` or run interactively to publish.\"\n\n### 5. Silence unless there's a result or blocker\n\nHeadless mode emits **at most one message** per run, on one of these channels (in priority order — pick the first that's wired):\n\n1. The customer's preferred notification channel (`profile.md → notify_url` if set — ntfy.sh, Slack incoming webhook, email-via-mailgun, etc.). Not yet a populated field; document it in the schema and ignore until set.\n2. `~/.buck-mason-stylist/runs/<lookbook_id>/summary.md` — a file the customer reads later.\n3. Stdout of the running process — picked up by whatever scheduler invoked the agent (cron, systemd timer, `/loop` log, OpenClaw routine output).\n\n**No mid-run progress chatter.** No \"I'm searching the catalog now…\", no \"Generated look 1, working on look 2…\". Just the final summary or blocker.\n\n## The run summary format\n\nOne markdown block, ≤ 25 lines, no preamble:\n\n```markdown\n# <Lookbook title> — <date>\n\n✅ Deployed: <URL>\nTier: <Premium | Editorial | Minimum>\nLooks: <N>\nPieces: <total count> · in stock <%>\nSubtotal at pick: $<total>\n\nNotes\n- <one line per noteworthy thing — pieces dropped, low-stock warnings, fallback tier reasons>\n\nVerify: opengraph.xyz/url/<encoded-url>\n```\n\nFailure form:\n\n```markdown\n# <Lookbook title> — <date>\n\n❌ BLOCKER: <one-line reason>\nTier attempted: <Premium | Editorial | Minimum>\nStage failed: <fetch | tryon | build | validate-local | deploy | validate-deployed>\n\nDetail\n<short paragraph with the underlying error and what would unblock>\n```\n\n## Wiring it together — the canonical headless invocation\n\n```bash\n# Run as a /loop or cron job. The script bundle (scripts/) is deterministic\n# and headless-safe by construction. Always invoke via the explicit\n# interpreter — ClawHub installs may strip the executable bit on unpack.\npython3 scripts/build-html-lookbook.py \\\n  --config \"$RUN_DIR/config.json\" \\\n  --picks  \"$RUN_DIR/picks.json\" \\\n  --look-images \"$RUN_DIR/looks/\" \\\n  --out \"$RUN_DIR/deploy/\"\n\nbash    scripts/deploy-lookbook.sh \"$RUN_DIR/deploy/\" \"$PROJECT_NAME\" \\\n  --auto --no-overwrite --kv-id \"$LOOKBOOK_VOTES_KV_ID\"\n\npython3 scripts/validate-lookbook.py --dir \"$RUN_DIR/deploy/\" \\\n  --url \"https://${PROJECT_NAME}.pages.dev/\"\n```\n\nThe single canonical command that composes all of the above is **`scripts/run-headless-lookbook.py`** (orchestrator: discover → curate → build → deploy → validate → summary). For one-shot headless runs prefer that — see § \"The end-to-end orchestrator\" below. The orchestrator passes `profile.md → lookbook_votes_kv_id` through to the deploy wrapper; without that value or `$LOOKBOOK_VOTES_KV_ID`, the deploy blocks because voting is the default.\n\nIf any step's exit code is non-zero, the run summary file is the failure form. If all three succeed, the summary file is the success form and (if a `notify_url` is wired) the agent posts the summary to that channel.\n\n## Recurring weekly newsletter\n\nThe canonical recurring use of headless mode: a once-weekly lookbook surfacing what's new on buckmason.com plus anything the customer hasn't been pitched yet. Cadence configurable; weekly is the default.\n\n### What it shows\n\nPer run, the agent assembles 4–6 pieces grouped into 1–2 looks, drawn from this priority order:\n\n1. **`/seasonal?days=14`** — products set live on buckmason.com in the last two weeks. Primary source.\n2. **General catalog** filtered to items **not present in `~/.buck-mason-stylist/wishlist.jsonl`** (by `sku`). Backfill when (1) is thin.\n3. **Style-ethos / color-prefs filter** applied across both sources — keep only items that match the customer's `style_ethos` and `favorites` colors. Drop anything in their `avoid` list.\n\nThe output is **always Editorial tier** by default — no gpt-image-2 spend on a recurring artifact (~$0.40/week × 52 = $21/year, not justifiable for a newsletter that may or may not be opened). The customer can opt into Premium tier for the weekly via `profile.md → weekly_lookbook_tier: premium` if they want try-on imagery every week.\n\n### URL stability is non-negotiable\n\nEach weekly run gets **its own permanent Cloudflare Pages project** so the customer can refer back to any past edition forever. See `references/hosting-options.md` § \"URL stability — one Pages project per permanent lookbook\" for the full rule and the `--no-overwrite` flag. Project naming: `<lookbook_project_prefix>-weekly-<YYYY-WW>` (ISO week number).\n\n### Dedup against the wishlist\n\n`~/.buck-mason-stylist/wishlist.jsonl` is the long-term memory. Every piece proposed in any prior lookbook (whether the customer bought it or not) gets a row at proposal time:\n\n```jsonl\n{\"sku\":\"BM13211.679NATL\",\"name\":\"Natural Draped Linen Deuce Coupe Camp Shirt\",\"size\":\"L\",\"lookbook_id\":\"2026-05-09-mellow-la\",\"lookbook_url\":\"https://buckmason-nick-2026-05-09-mellow-la.pages.dev/\",\"proposed_at\":\"2026-05-09T14:32:00Z\"}\n```\n\nFields are added on top of the existing wishlist contract (item + size + qty + order_id + purchased_at):\n\n- `proposed_at` — when the piece first appeared in any lookbook (UTC ISO timestamp). The dedup key for \"have I shown this to the customer before?\"\n- `lookbook_url` — permanent URL of the lookbook that introduced the piece. Lets the agent answer \"where did you suggest the camp shirt?\" without re-fetching.\n- `purchased_at` / `order_id` — set later (nullable until the customer actually buys via MPP checkout).\n\nThe newsletter dedupes on `sku` — if a piece was proposed last month and the customer didn't buy it, don't re-propose unless **both**: (a) it's been ≥ 8 weeks since `proposed_at`, AND (b) the agent has explicit reason to re-surface (price drop, restocked after sellout, customer asked for it). Default behavior: skip.\n\n### Canonical invocation\n\n```bash\n# Discover candidates (deterministic — no LLM, no network beyond MCP).\npython3 scripts/discover-weekly-candidates.py \\\n  --gender m \\\n  --since-days 14 \\\n  --wishlist ~/.buck-mason-stylist/wishlist.jsonl \\\n  > candidates.json\n\n# Agent step (taste): read candidates.json, pick 4–6 items, group into 1–2\n# looks, write picks.json + config.json with a unique lookbook_id.\n# (No script for this — the agent's call.)\n\n# Build, deploy with --no-overwrite for URL stability, validate.\nRUN_DIR=~/.buck-mason-stylist/runs/weekly-2026-19\npython3 scripts/build-html-lookbook.py --no-tryon \\\n  --config \"$RUN_DIR/config.json\" --picks \"$RUN_DIR/picks.json\" \\\n  --out    \"$RUN_DIR/deploy/\"\n\nPROJECT=\"${LOOKBOOK_PROJECT_PREFIX}-weekly-2026-19\"\nbash scripts/deploy-lookbook.sh \"$RUN_DIR/deploy/\" \"$PROJECT\" \\\n  --auto --no-overwrite --kv-id \"$LOOKBOOK_VOTES_KV_ID\"\n\n# Append every proposed piece to the wishlist with proposed_at + lookbook_url.\n# (Either inline or via a future scripts/log-proposal.py — keep agent-side.)\n```\n\n## The end-to-end orchestrator\n\nUse `scripts/run-headless-lookbook.py` for a single canonical headless invocation. It composes the deterministic chain (score → discover → curate → build → deploy → validate → summary), selects Premium for one-shot/event lookbooks when `OPENAI_API_KEY` and at least two reference photos are available, keeps recurring weekly runs Editorial unless opted into Premium, and writes a run-summary file at `~/.buck-mason-stylist/runs/<lookbook_id>/summary.md`.\n\n```bash\n# Weekly newsletter\npython3 scripts/run-headless-lookbook.py --weekly --profile ~/agent-workspace/profile.md\n\n# Event-driven (auto-scored; skips on hard-veto)\npython3 scripts/run-headless-lookbook.py --event /path/to/event.json --profile ~/agent-workspace/profile.md\n```\n\nThe orchestrator never deploys without `preferred_lookbook_host_auto: true` in the profile; when deploy authorization is missing it produces the lookbook locally (under `~/.buck-mason-stylist/runs/<lookbook_id>/deploy/`) and writes a summary saying \"ready to deploy — set `_auto: true` or run interactively to publish.\" That preserves the deploy-authorization rule even when an event scores 7–10 and would otherwise auto-generate AND auto-deploy.\n\nThe run summary (per § \"The run summary format\" above) reports the URL, the count of new products surfaced, and any reason items got dropped (out of stock, ethos mismatch, already proposed within 8 weeks).\n\n### Cadence + opt-out\n\n- Default cadence: weekly, Monday morning local time. Configurable via `profile.md → weekly_lookbook_cadence: \"weekly\" | \"biweekly\" | \"monthly\" | \"off\"`.\n- `off` disables the recurring lookbook entirely; the agent still runs event-driven (calendar) lookbooks per `references/event-suitability.md`.\n- The first weekly run after install is the \"welcome\" lookbook — it can use the entire `/recommend` capsule (not just recently-live), since the customer has no wishlist history yet.\n\n## Per-run dev logging\n\nEvery orchestrator run writes two timing artifacts to its run directory:\n\n```\n~/.buck-mason-stylist/runs/<lookbook_id>/\n├── _timings.jsonl    one JSON line per phase, machine-readable\n└── _run.log          human-readable chronological trace + slowest-phases summary\n```\n\nPlus a one-line stderr summary at end-of-run:\n\n```\n[timings] total 15.9s · slowest: discover_candidates 10.2s | build 5.7s | validate_local 0.0s\n```\n\n### What gets timed\n\n| Phase | What's measured | Typical range |\n|---|---|---|\n| `parse_profile` | YAML/markdown parse of `profile.md` | <0.1s |\n| `score_event` (event mode only) | `scripts/score-calendar-event.py` regex pass | <0.1s |\n| `discover_candidates` | `scripts/discover-weekly-candidates.py` — MCP `/products?recently_live` + backfill + per-candidate `/products/<id>` detail calls | 5–30s (depends on candidate count and MCP latency) |\n| `build` | `scripts/build-html-lookbook.py` — thumbnail downloads (curl per piece) + og.jpg generation + HTML render | 3–10s |\n| `verify_face` (Premium tier only, per look) | `scripts/verify-face.py` — GPT-4o-vision call | 5–15s per look |\n| `validate_local` | `scripts/validate-lookbook.py --dir` | <0.1s |\n| `deploy` (when `_auto: true`) | `scripts/deploy-lookbook.sh` — wrangler probe + (idempotent) project create + upload + post-deploy validate | 10–30s |\n| `validate_deployed` | `scripts/validate-lookbook.py --url` (D1–D6 checks) | 2–5s |\n| `wishlist_append` | JSONL append to `~/.buck-mason-stylist/wishlist.jsonl` | <0.1s |\n\n### When to look at the log\n\n- **Run took longer than expected** — check `_run.log`. Top-3 slowest phases are at the bottom; the chronological list is in the middle.\n- **Phase consistently slow** — grep across many runs:\n  ```bash\n  jq -s 'map(select(.phase == \"discover_candidates\")) | sort_by(.duration_s) | reverse | .[0:5]' \\\n    ~/.buck-mason-stylist/runs/*/_timings.jsonl\n  ```\n- **Phase failed** — entries with `\"ok\": false` carry an `error` field with the exception class + first 200 chars. Same data shows in `_run.log` with a `✗` mark.\n\n### What's NOT in the orchestrator's timing\n\n- **`gpt-image-2` generation for Premium-tier runs.** That call lives outside the deterministic chain — the agent makes it between the first orchestrator pass (which exits with `READY_FOR_PREMIUM_IMAGE_STEP`) and the second pass (which resumes via `--resume-build`). If a \"full Premium run\" feels like 5+ minutes, the time is likely there: gpt-image-2 high-quality 1024×1536 takes ~30–60s per look × 2 looks = 1–2 minutes, plus the `verify_face` overhead (10–30s for two looks). The orchestrator's `_run.log` only spans the orchestrator's portion of work.\n- **Wrangler interactive prompts.** First-time `wrangler login` for a fresh machine isn't timed (it's outside the orchestrator).\n- **Per-candidate MCP `/products/<id>` calls** inside `discover_candidates` — currently rolled up into the parent phase. If `discover_candidates` is the bottleneck, the next instrumentation level is to add timings inside `discover-weekly-candidates.py` itself.\n\n## What headless mode does NOT do\n\n- **Doesn't run MPP checkout autonomously.** Even with `_auto: true`, MPP requires the customer's push-approval in their Link app — that's the consent step. A scheduled run produces a lookbook + handoff prose, but checkout always waits for the customer.\n- **Doesn't email customers from the agent's address.** Sending email or chat messages on the customer's behalf is a separate authorization (`notify_url` wires the final summary; it doesn't email \"your stylist suggests…\" to anyone).\n- **Doesn't bootstrap accounts.** If `wrangler` / `link-cli` / OPENAI key is missing, the run produces a blocker summary asking the customer to set it up next time they're interactive — the agent never tries to sign up on their behalf.\n\n## Composition with other docs\n\n- `references/hosting-options.md` — the deploy-authorization rules originate there (`preferred_lookbook_host_auto` field). Headless mode is the surface that consumes it.\n- `references/acceptance-checklist.md` — every headless run gates on the validator. Failed gate → blocker summary.\n- `templates/profile.schema.json` — defines the fields headless mode reads (`preferred_lookbook_host*`, `link_payment_method`, `notify_url` once wired).\n\nFile v0.7.2:references/hosting-options.md\n\n# Hosting the HTML lookbook online\n\nOnce `lookbook.html` (or `lookbook-cart.html`) is on disk, the customer often wants a link to share — for a stylist review, an SO, an email, or just to open on their phone. This doc is the **capability-aware menu** the agent walks before publishing.\n\n## Why this exists\n\nThe naive approach is a static decision tree (\"if you have `wrangler`, use Cloudflare Pages\"). That's the wrong shape for an agent — it assumes the customer knows what tools they have and what those tools' auth status is. The right shape: **probe the runtime, rank the transports the agent can actually use right now, present the top one, fall through cleanly on failures.** The customer should never need to know what `wrangler` is.\n\n## The transport scoreboard\n\nIn priority order (best first). Each row's **probe** is the one-shot check the agent runs to know whether the transport is usable *without prompting the customer for setup*. A transport that's installed but unauthenticated is a soft-no — fall through, don't try to bootstrap.\n\n| Rank | Transport | Probe (returns 0 = available) | Deploy command | URL shape | Persistence | Notes |\n|---|---|---|---|---|---|---|\n| 1 | **Cloudflare Pages** | `command -v wrangler && wrangler whoami 2>/dev/null` | Two-step: see § per-transport detail | Per-deploy `https://<deploy>.<project>.pages.dev` + stable alias `https://<project>.pages.dev` | Permanent | Free tier generous; fast CDN; custom domain optional. **Wrangler v4+ does NOT auto-create the project on first deploy** — `wrangler pages project create` runs once per account before the first `pages deploy`. Subsequent deploys are ~5s. |\n| 2 | **Netlify** | `command -v netlify && netlify status --json 2>/dev/null \\| jq -e .siteData` | `netlify deploy --dir=. --prod` (after a one-time `netlify init`) | `https://<deploy>--<site>.netlify.app` | Permanent | Equivalent polish to CF Pages. |\n| 3 | **Vercel** | `command -v vercel && vercel whoami 2>/dev/null` | `vercel deploy --yes --prod ./lookbook.html` (or from a dir) | `https://<deploy>.vercel.app` | Permanent | Auto-detects static. First run is interactive (\"link to existing project?\") — after that it's fast. |\n| 4 | **Surge** | `command -v surge && [ -f ~/.surge/config.json ]` | `surge ./ <subdomain>.surge.sh` | `https://<sub>.surge.sh` | Persistent | Single-file friendly. Free, no project setup. |\n| 5 | **GitHub Gist + htmlpreview** | `command -v gh && gh auth status 2>/dev/null` | `gh gist create lookbook.html --public --desc \"Buck Mason lookbook\"` | `https://htmlpreview.github.io/?<gist-raw-url>` | Persistent, versioned | Renders through a third-party preview wrapper (htmlpreview.github.io); Google may index eventually. |\n| 6 | **AWS S3 (static)** | `command -v aws && aws sts get-caller-identity 2>/dev/null` *plus* a usable bucket (see Gotchas) | `aws s3 cp lookbook.html s3://<bucket>/<key>.html --acl public-read --content-type text/html` | `https://<bucket>.s3.amazonaws.com/<key>.html` | Permanent | High false-positive rate on the probe — having `aws` configured ≠ having a public-read static-hosting bucket. Use only when the customer has previously specified a bucket via `profile.md → preferred_lookbook_host_config.s3_bucket`. |\n| 7 | **0x0.st (anonymous)** | always available (any system with `curl`) | `curl -F \"file=@lookbook.html;type=text/html\" https://0x0.st` | `https://0x0.st/<id>.html` | ~30 days minimum (longer for smaller files) | Universal fallback. No auth, no setup. Anonymous + public — anyone with the URL can view. |\n\n**Future option — on-brand Pima preview** (`POST /mcp/buckmason/preview` returning `https://www.buckmason.com/p/<short-id>`). Not implemented today; flag as a follow-up if the customer asks for a `*.buckmason.com` URL.\n\n## The probe script\n\nOne-shot bash that the agent can run before publishing to find out which transports are live in this runtime. Returns a list of available options, ranked.\n\n```bash\n#!/usr/bin/env bash\n# Returns one transport name per line, in priority order, only those usable\n# right now without further auth setup. Empty output = only 0x0.st available\n# (which is always implicit, so add it as the universal fallback).\n\nprobe() {\n  case \"$1\" in\n    cloudflare-pages) command -v wrangler >/dev/null 2>&1 && wrangler whoami >/dev/null 2>&1 ;;\n    netlify)          command -v netlify  >/dev/null 2>&1 && netlify status --json 2>/dev/null | grep -q '\"siteData\"' ;;\n    vercel)           command -v vercel   >/dev/null 2>&1 && vercel whoami >/dev/null 2>&1 ;;\n    surge)            command -v surge    >/dev/null 2>&1 && [ -f \"$HOME/.surge/config.json\" ] ;;\n    gist)             command -v gh       >/dev/null 2>&1 && gh auth status >/dev/null 2>&1 ;;\n    s3)               command -v aws      >/dev/null 2>&1 && aws sts get-caller-identity >/dev/null 2>&1 ;;\n    *)                return 1 ;;\n  esac\n}\n\nfor t in cloudflare-pages netlify vercel surge gist s3; do\n  probe \"$t\" && echo \"$t\"\ndone\necho \"0x0\"   # universal fallback\n```\n\nThe agent runs this, picks the top line, and proceeds. Probing all six takes <1s on a typical dev box.\n\n## Per-transport detail\n\n### Cloudflare Pages (rank 1)\n\nWrangler v4 split project creation from deployment — **the project must exist before the first `pages deploy`**, or you get `Project not found [code: 8000007]`. Two-step flow, with the create step idempotent (safe to skip if the agent has previously deployed to this account):\n\n```bash\n# 1. One-time per account: create the Pages project. Idempotent-ish — re-running\n#    on an existing project errors but doesn't break anything. Probe first.\nif ! wrangler pages project list 2>/dev/null | grep -q '\\bbuckmason-lookbook\\b'; then\n  wrangler pages project create buckmason-lookbook --production-branch main\nfi\n\n# 2. Per deploy: copy the artifact + og.jpg into a directory, deploy.\nmkdir -p _cf-deploy\ncp lookbook.html _cf-deploy/index.html\ncp lookbook-og.jpg _cf-deploy/og.jpg     # see references/output-formats.md\nwrangler pages deploy _cf-deploy \\\n  --project-name buckmason-lookbook \\\n  --branch main \\\n  --commit-dirty=true                    # otherwise wrangler nags about uncommitted output\n\n# → Per-deploy URL: https://<sha>.buckmason-lookbook.pages.dev\n# → Stable alias  : https://buckmason-lookbook.pages.dev   ← embed THIS in og:url + og:image\n```\n\n`wrangler login` once per machine (browser OAuth). The token grants `pages (write)` among other scopes — confirm with `wrangler whoami`. The stable alias (`<project>.pages.dev`, no per-deploy prefix) is what survives across deploys, so that's the URL that goes into the OG meta tags before deploy. To bind a custom domain (e.g., `lookbook.buckmason.com`), `wrangler pages domain add` once; every subsequent deploy is then reachable at the custom domain too.\n\n**Cleanup:** `wrangler pages project delete buckmason-lookbook` removes the project + all deploys. No surprise costs on the free tier; teardown is just hygiene.\n\n### Netlify (rank 2)\n\n```bash\nnetlify deploy --dir=. --prod   # from the dir containing lookbook.html\n```\n\nFirst-time: `netlify init` (interactive — links the dir to a site, asks for the team). After init, `netlify deploy --prod` is one-shot.\n\n### Vercel (rank 3)\n\n```bash\nvercel deploy --yes --prod ./lookbook.html   # or from a directory\n```\n\nFirst-time: `vercel login` (browser/email). First `vercel deploy` from a fresh dir prompts \"link to existing project? scope?\" — that's the setup beat. Subsequent deploys are non-interactive. `--yes` accepts defaults on subsequent runs but doesn't fully bypass first-run setup.\n\n### Surge (rank 4)\n\n```bash\nmkdir -p _surge && cp lookbook.html _surge/index.html\nsurge _surge nick-spring-2026.surge.sh\n```\n\nOne-time email signup the first run. After that, fully non-interactive — pass `--domain <sub>.surge.sh` to skip the prompt. Persistent indefinitely; the subdomain belongs to the account.\n\n### GitHub Gist + htmlpreview (rank 5)\n\n```bash\ngist_url=$(gh gist create lookbook.html --public --desc \"Buck Mason lookbook\" 2>&1 | tail -n1)\nsha=$(basename \"$gist_url\")\nuser=$(gh api user -q .login)\necho \"https://htmlpreview.github.io/?https://gist.githubusercontent.com/$user/$sha/raw/lookbook.html\"\n```\n\n`gh auth login` once. The htmlpreview wrapper is a third-party (github.io subdomain) but well-established. Versioned for free — `gh gist edit` updates in place.\n\n### AWS S3 (rank 6)\n\n```bash\naws s3 cp lookbook.html s3://<bucket>/<key>.html \\\n  --acl public-read --content-type text/html\necho \"https://<bucket>.s3.amazonaws.com/<key>.html\"\n```\n\nThe probe (`aws sts get-caller-identity`) only confirms credentials, not the existence of a writable public bucket. Treat S3 as **opt-in only** — require `profile.md → preferred_lookbook_host_config.s3_bucket` to be set explicitly. Don't try to create a bucket on the fly; bucket policies + ACLs are easy to misconfigure into accidentally-public-everything.\n\n### 0x0.st (rank 7, universal fallback)\n\n```bash\nurl=$(curl -sS -F \"file=@lookbook.html;type=text/html\" https://0x0.st)\necho \"$url\"   # → https://0x0.st/aBcD.html\n```\n\nZero auth, zero setup. The catch: ephemeral (~30 days for typical lookbook sizes), anonymous, public. Fine for \"show my friend right now\"; not fine for \"I want to open this in a year.\" Always tell the customer about the ~30-day expiry before using.\n\n## Design rules\n\n### Confirm before publishing\n\nHosting an artifact publicly is a one-way action — once the URL is live, anyone with it can view. **Always confirm with the customer before deploying**, with the URL pattern named (\"This will publish at `nick-spring-2026.surge.sh` — anyone with that link can view your lookbook including try-on photos. Go?\"). Surge / Pages / Netlify / Vercel URLs are all guess-resistant but not private; treat them as public-by-URL.\n\nThe exception: if `profile.md → preferred_lookbook_host_auto: true` is set AND the chosen transport is one the customer has previously approved on this profile, the agent can skip the per-publish confirm. Default is to ask every time.\n\n### Sticky preference in `profile.md`\n\nAfter the first publish on a runtime, persist the chosen transport so the agent doesn't re-probe and re-ask every time:\n\n```yaml\npreferred_lookbook_host: cloudflare-pages       # auto-set after first successful publish\npreferred_lookbook_host_auto: false              # if true, agent skips per-publish confirm\npreferred_lookbook_host_config:\n  cloudflare_pages_project: buckmason-lookbook   # transport-specific config (optional)\n  s3_bucket: my-personal-static                  # required if rank-6 S3 is preferred\n```\n\nWhen `preferred_lookbook_host` is set and still available (probe still returns it), use it directly. If it's no longer available (auth expired, CLI uninstalled), re-probe and present the new top option as a one-time switch (\"`wrangler` is unavailable on this machine; switching to `netlify` for this lookbook — keep that as the new default?\").\n\n### Sensitivity warning at host time\n\nThe lookbook can include AI-generated try-on photos of the customer. Before any publish to a public-by-URL host, surface the trade-off in plain English: \"The link is unguessable but public — anyone with it can view your face + the generated outfits. For a private-only artifact, stick with the local file or the `images` format and skip hosting.\" The customer can always refuse and keep the file local.\n\nWhen a future on-brand Pima preview endpoint lands (`POST /mcp/buckmason/preview` → `buckmason.com/p/<id>`), it'll likely support an authenticated-viewer mode (login required) — at that point, the right default for try-on lookbooks shifts from public-by-URL to login-required.\n\n### \"Tool installed but unconfigured\" is a soft-no\n\nEach probe in the scoreboard checks both *the CLI exists* and *it has a usable auth/config*. A transport where the CLI exists but the auth is missing (e.g., `wrangler` is installed but `wrangler whoami` errors) **fails the probe** and the agent falls through to the next transport. **Don't try to bootstrap auth from inside the agent flow** — it's interactive (browser logins, email codes), it's slow, and it's a different mental task for the customer than \"host my lookbook.\" If the customer wants to set up `wrangler`, that's a separate conversation; offer it as a side-suggestion (\"you have `wrangler` installed but unauthenticated — `wrangler login` once would let me default to Cloudflare Pages from now on\") only after the lookbook is already hosted via the next-best transport.\n\n### Don't auto-create cloud accounts\n\nEven when CLIs offer \"sign up from CLI\" flows (Surge does this on first deploy with an email; Vercel does email-magic-link), **don't run those non-interactively on the customer's behalf**. Account creation has terms-of-service implications and is one of the explicit-permission-required actions in the skill's safety contract (`SECURITY.md`). If a probe reveals \"CLI installed, no account,\" route to the next transport instead.\n\n## Social-preview meta tags (`og:image`, Twitter Card)\n\nWhen the agent posts the hosted URL into iMessage / Slack / Discord / Twitter / a generic chat client, the chat client unfurls the link and renders a preview tile. **The lookbook must include `og:image` and `twitter:image` meta tags pointing at an absolute URL of the hero image, or the unfurl will be a sad untitled grey box.** The HTML skeleton in `output-formats.md` already includes the full meta-tag set with `{{ABSOLUTE_PAGE_URL}}` and `{{ABSOLUTE_OG_IMAGE_URL}}` placeholders — the agent's job is to fill those in correctly per transport.\n\n### The og.jpg artifact\n\nThe lookbook builder generates **one extra file alongside the HTML**: `lookbook/<date>-<event>-og.jpg`, sized **1200×630 (the OG / Twitter Card standard, 1.91:1)**. Source it from Look 01's hero (or whichever look the customer flagged as the cover) and **letterbox on a white background** rather than crop — Buck Mason's product photography is 3:4 portrait, and cropping the model out of frame is worse than a bit of white. Pillow recipe:\n\n```python\nfrom PIL import Image\nhero = Image.open('lookbook/2026-04-26-sonoma-look-1.png').convert('RGB')\ncanvas = Image.new('RGB', (1200, 630), (255, 255, 255))\nratio = min(1200 / hero.width, 630 / hero.height)\nnew_w, new_h = int(hero.width * ratio), int(hero.height * ratio)\ncanvas.paste(hero.resize((new_w, new_h), Image.LANCZOS),\n             ((1200 - new_w) // 2, (630 - new_h) // 2))\ncanvas.save('lookbook/2026-04-26-sonoma-og.jpg', 'JPEG', quality=85, optimize=True)\n```\n\nOutput is typically 80–180 KB — small enough to ship with every deploy, large enough to look sharp in a preview.\n\n### Resolving `{{ABSOLUTE_PAGE_URL}}` and `{{ABSOLUTE_OG_IMAGE_URL}}` per transport\n\n| Transport | Page URL | og.jpg URL | Strategy |\n|---|---|---|---|\n| **Cloudflare Pages** | `https://<deploy>.<project>.pages.dev/` | `https://<deploy>.<project>.pages.dev/og.jpg` | **Multi-file deploy.** Copy both `lookbook.html` (as `index.html`) and `og.jpg` into a directory, deploy the directory. Both URLs resolve cleanly. The deploy URL pattern is predictable from the project name — substitute placeholders BEFORE deploy. |\n| **Netlify** | `https://<deploy>--<site>.netlify.app/` | `https://<deploy>--<site>.netlify.app/og.jpg` | Multi-file deploy. Same as above. The site name is fixed once `netlify init` runs, so the URL is predictable. |\n| **Vercel** | `https://<deploy>.vercel.app/` | `https://<deploy>.vercel.app/og.jpg` | Multi-file deploy. Same as above. The project name is fixed; deploy alias is predictable. |\n| **Surge** | `https://<sub>.surge.sh/` | `https://<sub>.surge.sh/og.jpg` | Multi-file deploy. Same as above — `surge ./dir <sub>.surge.sh` deploys the directory. |\n| **GitHub Gist + htmlpreview** | `https://htmlpreview.github.io/?https://gist.githubusercontent.com/<user>/<sha>/raw/lookbook.html` | Upload `og.jpg` to a separate Gist (binary files in Gist are awkward — use a separate transport) | **Two-step.** Upload `og.jpg` to 0x0.st first (`curl -F file=@og.jpg https://0x0.st` → URL), substitute into the HTML, then `gh gist create lookbook.html`. Or skip OG image and accept a textual unfurl on this transport. |\n| **AWS S3** | `https://<bucket>.s3.amazonaws.com/<key>.html` | `https://<bucket>.s3.amazonaws.com/<key>-og.jpg` | Multi-file: upload both with `--acl public-read --content-type` set correctly (`text/html` for the page, `image/jpeg` for og.jpg). |\n| **0x0.st** | `https://0x0.st/<id>.html` | `https://0x0.st/<other-id>.jpg` (separate upload) | **Two-step.** Upload `og.jpg` first → get URL → substitute into the HTML → upload the HTML. Both URLs persist together as long as the files exist. |\n\n### The pre-deploy substitution flow\n\nFor every transport, the agent's pre-deploy step is the same shape:\n\n1. Build `lookbook.html` with `{{ABSOLUTE_PAGE_URL}}` and `{{ABSOLUTE_OG_IMAGE_URL}}` placeholders intact.\n2. Build `og.jpg` (Pillow recipe above).\n3. Compute the page URL and og:image URL based on the transport. For multi-file transports, both URLs are derived from the deploy URL + filename. For two-step transports, upload `og.jpg` first to get its absolute URL.\n4. `sed -i '' \"s|{{ABSOLUTE_PAGE_URL}}|$page_url|g; s|{{ABSOLUTE_OG_IMAGE_URL}}|$og_url|g\" lookbook.html` (or equivalent string replacement).\n5. Deploy.\n\nFor Cloudflare Pages the per-deploy `<deploy>.<project>.pages.dev` URL is a fresh subdomain each time. To avoid the chicken-and-egg, **bind a stable Pages alias** (`wrangler pages project create buckmason-lookbook` once + use the project's primary domain `https://buckmason-lookbook.pages.dev` rather than the per-deploy URL) — substitute that, deploy, every deploy serves the same primary URL.\n\n### Shopify-CDN fallback for og:image\n\nIf the builder can't generate an `og.jpg` for whatever reason (no Pillow, sandbox restrictions, image-gen failure), fall back to **a representative product flat-lay URL from `cdn.shopify.com`** — the host is already declared in `clawhub.json#permissions.network`, and Shopify's CDN URLs are public, stable, and fast. Lower-fidelity (the preview won't show the customer's try-on, just a flat-lay garment) but always works without an extra hosting step. Pick the `try_on` field from `GET /mcp/buckmason/products/<slug>/imagery` for Look 01's hero piece.\n\n### Verifying the unfurl before sharing\n\nOnce the lookbook is hosted, hit `https://www.opengraph.xyz/url/<encoded-deploy-url>` (or any OG validator) to confirm the preview renders. Slack and Twitter cache aggressively — if the customer notices a stale preview after re-deploying, they can force a recrawl in Slack with `/unfurl <url>` or wait for the cache to expire (~24h on most platforms).\n\n### When to skip OG entirely\n\n- **Local file delivery** (no hosting) — no URL means no unfurl.\n- **`images` format** — no HTML, no point.\n- **PDF / PPT** — different unfurl model (the chat client previews the file itself, not via OG meta).\n- **0x0.st with `--no-og` customer preference** — if the customer says \"I don't want a preview, just the link,\" skip the og.jpg upload step and leave the placeholders unresolved. Most clients fall back to a textual unfurl.\n\n## URL stability — one Pages project per permanent lookbook\n\n**The default Cloudflare Pages model overwrites the stable alias on every deploy.** When you `wrangler pages deploy <dir> --project-name foo` repeatedly, `https://foo.pages.dev/` always serves the latest deploy — the previous one is no longer reachable at that URL (per-deploy URLs at `https://<deploy-sha>.foo.pages.dev/` do persist, but they're ugly and not what the customer bookmarks). For test/iteration this is fine — `buckmason-stylist-test` is the canonical \"rebuild and replace\" project. **For permanent customer-facing lookbooks (weekly newsletter, event-driven generations the customer will refer back to), each lookbook gets its own project**:\n\n```bash\n# Pattern: buckmason-<customer-handle>-<lookbook-id>\nPROJECT=\"buckmason-nick-2026-05-09-mellow-la\"\nbash scripts/deploy-lookbook.sh ./deploy \"$PROJECT\" --auto --no-overwrite\n# → https://buckmason-nick-2026-05-09-mellow-la.pages.dev/\n```\n\nThe customer can bookmark, share, or revisit any URL ever generated this way and it will keep working — Cloudflare doesn't garbage-collect inactive Pages projects on the free tier. Fifty-two weekly lookbooks per year × multiple years × all event-driven runs adds up to a few hundred projects over time, well within the Pages free-tier project quota (currently 100 active per project but unlimited inactive — verify against the customer's Cloudflare dashboard before going long-running).\n\n### `--no-overwrite` flag\n\n`scripts/deploy-lookbook.sh --no-overwrite` aborts if the named project already has a prior deployment. **Use it on every customer-facing recurring run** to make the URL-stability guarantee load-bearing — if the script ever silently overwrites a project the customer expected to be permanent, the bookmark breaks. Test/iteration scripts (which intentionally rebuild on the same project) just don't pass the flag.\n\n### Naming convention\n\n| Lookbook kind | Project name pattern |\n|---|---|\n| Test / iteration | `buckmason-stylist-test` (or any short fixed name — overwritten freely) |\n| Event-driven (one-off) | `buckmason-<customer>-<lookbook-id>` (e.g. `buckmason-nick-2026-05-09-mellow-la`) |\n| Recurring (weekly newsletter) | `buckmason-<customer>-weekly-<YYYY-WW>` (e.g. `buckmason-nick-weekly-2026-19`) |\n\nCustomer's preferred prefix lives in `profile.md → lookbook_project_prefix` (default `buckmason-<email-handle>`). Append the `lookbook_id` from the build config — same string the file is filed under. Both segments are kebab-case; the URL ends up readable by humans.\n\n## Quick decision tree (when probing isn't worth it)\n\nFor a one-shot ask where the customer has already named a transport:\n\n| Customer intent | Use |\n|---|---|\n| \"Just send my friend right now\" | 0x0.st |\n| \"I want to keep it forever\" | Surge or Cloudflare Pages |\n| \"Branded subdomain on buckmason.com\" | wait for the Pima preview endpoint |\n| \"Send to a Buck Mason customer / anyone external\" | Cloudflare Pages with custom subdomain |\n| \"I'll just airdrop the file\" | Skip hosting entirely — hand over the local `.html` |\n\nThis tree is for the customer who already has a preference. The default is still: probe, rank, ask once.\n\n## When to load this doc\n\n- Whenever the customer asks for a hosted link to their lookbook (`html` or `html-cart`).\n- Skip when the customer wants the local file only (`images`, `ppt`, or \"just save it\").\n\nFile v0.7.2:references/image-generation.md\n\n# Image generation: try-on + lookbook\n\nThis skill produces three kinds of imagery using OpenAI's image API (`gpt-image-2`, released 2026-04-21):\n\n1. **Try-on** — single photo of the customer wearing one outfit in one setting.\n2. **Lookbook** — 3–5 try-on images for the same trip/event, varied poses + framings, shareable as a single grid or PDF.\n3. **Flat-lay** *(fallback)* — collage of product images alone, no customer in frame, used when there's no reference photo.\n\n> **Companion docs:**\n> - `references/style-reasoning.md` — the *why* engine. Every garment in a lookbook needs a one-sentence rationale that touches climate fit, formality fit, and personal/classic angle. Render it next to each look in the lookbook output.\n> - Pull setting/composition strings from `GET /mcp/lookbook/settings?occasion=…&season=…&region=…` rather than hand-writing them. The endpoint returns curated `looks[]` with `setting`, `composition`, recommended `size` and `quality`. Use those verbatim in the SETTING and COMPOSITION blocks of the prompt template below.\n> - When the customer doesn't specify a setting, use a Buck Mason on-model lifestyle image as an additional reference (pulled from `GET /mcp/products/:id/imagery` → `hero` field) and tell the model \"match the backdrop and lighting of the last reference image.\" This anchors the scene in the brand's own visual language.\n\n## Inputs\n\nThe pipeline takes **two structured fact sheets** plus a setting, and assembles them into the prompt. Skipping detail in either fact sheet is the single biggest cause of identity drift and garment hallucination — fill them out before generating.\n\n| Input | Source | Required? |\n|---|---|---|\n| **Identity anchor photos** (2–3) | `profile.md` → `reference_photos` | Yes; without them, fall back to flat-lay (no try-on) |\n| **Build fact sheet** (height, weight, build, shoulder/torso/leg ratios, posture, age range) | `profile.md` → Build + Face sections | Yes |\n| **Face fact sheet** (hair, beard, eye color, skin tone, glasses, distinguishing features) | `profile.md` → Face section | Yes |\n| **Garment fact sheet** (per item: structure, fabric, fit, weight, color, construction) | `/mcp/products/:id` + `/mcp/products/:id/imagery` | Yes |\n| Product flat-lay images (one per garment) | `/mcp/products/:id/imagery` (`try_on` or `hero` field) | Yes |\n| Setting description | `/mcp/lookbook/settings?occasion=&season=&region=` | Yes |\n| Style notes (fit prefs, color avoid list) | `profile.md` → fit/color prefs, plus event dress code | Recommended |\n\n### Identity anchor photos — selection rules\n\n**Image input order matters.** Send garments FIRST, identity references AFTER. Reasoning: `gpt-image-2` treats the most photographically-complete reference image as \"the answer\" and tends to copy its outfit, backdrop, and pose verbatim. Garments-first reverses that bias.\n\n**Never include fully-dressed reference photos for try-on lookbooks.** This was learned the hard way: a clean LA street photo (subject in olive overshirt + white henley + black jeans against a brick wall, golden-hour light) was passed as identity anchor #1 in a 7-reference call. `gpt-image-2` produced 5 lookbook images that ALL copied that outfit, that backdrop, that lighting — completely ignoring the actual product flat-lays for shirt and pant, and the chateau / vineyard / dinner setting prompts. Use **only** these reference types:\n\n- **Clean front-facing portrait** — head-and-shoulders, neutral expression, even lighting, no sunglasses, no hat. This is the identity anchor.\n- **Shirtless full-body** (1–2 shots from different angles) — for true build under clothing. Without these, the model invents a generic male body.\n- **Optional**: a contextual face shot (daytime sunglasses on, low light, etc.) for lighting/expression range.\n\nDo NOT include:\n- Full-body shots in clean settings with the customer fully dressed (the model will copy the outfit + backdrop)\n- Photos in clothing visually similar to anything in the target outfit (it will just resize what's there)\n- Group photos (extra people will appear in the generated image)\n\nIf only one photo is available, ask for a second before proceeding. One photo gives the model freedom to generalize; two constrains it; three locks it down. The cost difference is negligible.\n\n### Garment image selection — the most common failure mode\n\nAlways use `GET /mcp/products/:id/imagery` and read these fields:\n\n- `try_on` — the safest image for image-gen (true flat-lay or close-up detail)\n- `try_on_is_flat` — boolean; `true` means it's a real flat-lay against a plain background, `false` means it's a tightly-cropped editorial detail\n- `try_on_warning` — populated when there's no good option (only on-model editorial exists)\n- `hero` — the marketing/editorial shot, usually on-model — **never use this for try-on**\n\n**Buck Mason's product image #1 is often an on-model editorial shot, not a flat-lay.** Why this matters: when you pass an editorial as the \"garment\" input to gpt-image-2, the model:\n\n1. Reads the secondary garments visible on the editorial model (e.g. their pants, their shoes) and uses them as input alongside the labeled garment\n2. Copies the model's face/build into the generated image as a second person standing alongside the customer\n3. Anchors the backdrop to the editorial's studio setting\n\nConcrete failure observed during this skill's bring-up: the Como Cashmere Tee (Faded Indigo) had no flat-lay; agent passed the position-1 editorial (Black male model in dark indigo tee + natural linen pants on a studio backdrop). The generated lookbook had the customer wearing the slate-navy tee with **natural linen pants** (copied from the editorial model) instead of the slate-navy sateen pant that was passed separately. Two looks also rendered with the editorial model as a second person beside the customer.\n\n**Mitigations, in order:**\n\n1. **Trust `try_on` over `hero`** — if `try_on_is_flat` is true, use it directly.\n2. **If only an editorial is available**, crop it programmatically to the garment region only:\n   ```bash\n   # Tee close-up: crop face out (top portion), keep the garment\n   magick editorial.jpg -crop 1800x1500+0+450 +repage tee-cropped.jpg\n   ```\n3. **Add this line to the IDENTITY block** when passing a garment image that contains a model: \"Image N may show a model wearing the garment. IGNORE the model's face, body, other garments, and the backdrop. Render ONLY the labeled garment on the actual subject.\"\n4. **Skip the try-on flow entirely** when `try_on_warning` indicates no usable image — fall back to a text-only lookbook with the editorial linked, not regenerated.\n\n### gpt-image-2 hint inventory — observed failure modes & their fixes\n\nEmpirically (verified across outfits 3–5), `gpt-image-2` will SILENTLY drop or substitute pieces unless you give it explicit hints. Each row below is a real failure observed during this skill's bring-up — wire the corresponding hint into the prompt for any look that risks it.\n\n| Failure mode | Concrete miss observed | Mitigation hint to wire into prompt |\n|---|---|---|\n| **Outer-layer dropped** | Asked for jacket-open-over-polo; rendered just the polo + pant, jacket gone (looks 5-2, 5-3 first pass) | Add a `⚠️ CRITICAL JACKET LOCK — NON-NEGOTIABLE` block above the GARMENT section: \"The OUTERMOST garment in this image MUST be the [name] from image 1. The image MUST clearly show its [collar / lapel / pocket / closure features]. If you cannot see those features, you have FAILED the brief.\" Also describe it as fully zipped/buttoned with only a small triangle of the layer below visible — \"open\" reads as optional and gets dropped. |\n| **Long-sleeve rendered short-sleeve** | Asked for LS plaid shirt; got SS camp shirt (look 5-1) | In the GARMENT block say \"LONG-SLEEVE button-up — sleeves clearly visible to the wrist, often rolled up to mid-forearm in editorial styling. The shirt is NOT a short-sleeve camp shirt.\" Add a sleeve-length sentence to the COMPOSITION block too. |\n| **Setting drift to studio** | Asked for \"moody window-light interior\" / \"Mediterranean cobblestone street\"; got generic clean studio backdrop in 70%+ of cases | gpt-image-2 has a strong studio prior. Mitigations: (1) make the SETTING block longer than the GARMENT block when a specific setting matters, (2) name 3+ concrete background elements (\"dark wood door, tan plaster wall, deep shadow on the left\"), (3) name the lighting register (\"mid-day sun raking from upper-left, warm golden bounce light\"), (4) end with \"The setting is NOT a studio backdrop.\" |\n| **Bottom-half clothing copied from editorial** | Position-1 image was an editorial of a model in tee + dark jeans; lookbook copied the dark jeans across all 5 looks | Crop the editorial to garment-only with `magick … -crop WxH+X+Y +repage`, OR use `try_on_is_flat` from `/mcp/products/:id/imagery` to pick a true flat-lay first. |\n| **Identity drift toward garment-reference model** | Customer rendered with the garment-reference model's skin tone, beard, hands | Always include the explicit IDENTITY HARD CONSTRAINTS block; restate \"The subject is [skin tone / age / hair]; do NOT render the model from any garment reference image as the subject.\" |\n| **Setting prompt overridden by reference photo backdrop** | LA street photo passed as identity ref → all 5 looks copied that brick-wall backdrop | Never include a fully-dressed reference photo with a strong backdrop in the identity slot. Only headshots / shirtless / face refs. |\n| **Hands/accessories materialize from garment ref model** | Wristwatch + ring + bracelet from editorial model showed up on customer who has none | In IDENTITY: \"Do NOT add jewelry, watches, bracelets, or rings unless they appear in the FACE/BUILD references.\" |\n| **Composition drifts to head-on portrait** | Asked for full-body candid lean; got static head-on bust shot | gpt-image-2 also has a strong head-on prior. Be explicit: \"Full body, three-quarter angle, weight on back leg, front foot crossed, head turned 30° to camera left.\" Naming the framing camera (\"35mm equivalent\" vs \"85mm equivalent\") helps too — 35mm pulls toward full body. |\n\n**General rule:** if the garment, sleeve length, or setting is non-negotiable, say so explicitly with capitals + \"MUST\" + \"FAILED the brief if not visible.\" Polite hints get dropped. Aggressive constraints get respected.\n\n### Garment fact sheet — what to extract\n\nFor each garment in a look, extract this into the prompt before posting to `/v1/images/edits`:\n\n| Field | Where to get it | Example |\n|---|---|---|\n| **Color** (named + visual) | `product.color.name` + `product.color.rgb` from `/mcp/products/:id` | \"Worn Chambray (faded mid-blue, ~#6E8AA3)\" |\n| **Fabric content** | `product.description_md` (parse for \"% linen / % cotton / % wool…\") | \"60% cotton / 40% linen\" |\n| **Weight** | `product.description_md` (search for gsm or oz/sq yd) | \"~180 gsm, lightweight summer-weight\" |\n| **Weave / knit** | description or category (oxford, poplin, twill, herringbone, jersey, ribbed knit, terry…) | \"plain weave, slight slub\" |\n| **Drape** | inferred from fabric + weight | \"fluid, soft drape, breathable\" |\n| **Silhouette / cut** | category + description (camp collar, spread collar, drop shoulder, straight leg, pleated front, etc.) | \"camp collar, short sleeve, single chest pocket, natural buttons\" |\n| **Construction details** | description (single/double-needle, contrast stitching, button material, pocket placement) | \"natural shell buttons, single-needle topstitch, slight curved hem\" |\n| **Fit on body** | product line's fit notation or company-defined fit | \"standard fit through chest and waist, regular sleeve length\" |\n| **Size on this customer** | `profile.md` size for the matching slot | \"size L (chest 41–43\\\")\" |\n\nIf the structured data isn't present (Buck Mason's item-master FY26 will add explicit fields), parse from `description_md` with a simple regex pass and **list what you couldn't extract** so the prompt is honest about the gaps.\n\n## API call\n\nUse OpenAI's image edits endpoint with multiple input images (`POST https://api.openai.com/v1/images/edits`):\n\n```bash\ncurl https://api.openai.com/v1/images/edits \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"model=gpt-image-2\" \\\n  -F \"image[]=@reference.jpg\" \\\n  -F \"image[]=@product1_flatlay.jpg\" \\\n  -F \"image[]=@product2_flatlay.jpg\" \\\n  -F \"prompt=$(cat prompt.txt)\" \\\n  -F \"size=1024x1536\" \\\n  -F \"quality=high\" \\\n  -F \"n=1\"\n```\n\nFor SDK use:\n\n```python\nfrom openai import OpenAI\nclient = OpenAI()\n\nresult = client.images.edit(\n    model=\"gpt-image-2\",\n    image=[open(\"reference.jpg\",\"rb\"), open(\"shirt.jpg\",\"rb\"), open(\"pant.jpg\",\"rb\")],\n    prompt=PROMPT,\n    size=\"1024x1536\",\n    quality=\"high\",\n    n=1,\n)\n# result.data[0].b64_json — decode and save\n```\n\nNotes:\n- `model`: **`gpt-image-2`** (snapshot `gpt-image-2-2026-04-21`) — required. The skill standardizes on `gpt-image-2`; do not fall back to `gpt-image-1` (identity drift, weaker garment-color fidelity, lower setting adherence). If the calling org isn't verified for `gpt-image-2`, surface that as an actionable error to the user rather than silently downgrading.\n- `size`: `1024x1536` for portrait try-ons, `1024x1024` for square lookbook tiles, `1536x1024` for landscape settings. `gpt-image-2` supports flexible sizes.\n- `quality`: use `high` for finals, `medium` for iteration drafts.\n- `n=1` per call — call repeatedly with varied prompts for a lookbook rather than asking for n>1 in one call (each image gets a distinct setting/pose).\n- `gpt-image-2` adds optional reasoning (\"thinking\" mode) which improves garment-fidelity and identity preservation for try-on; enable with `reasoning_effort: \"medium\"` if your client supports it.\n\n### Run multi-look generations in parallel — NOT sequentially\n\nEach look's image-edit call is independent: same identity anchors, same model, just a different garment + setting + composition. **Issue them concurrently** when generating a multi-look lookbook. A 3-look Premium run takes ~30–60s in parallel vs. ~90–180s sequentially — the wallclock saving is the difference between an interactive flow and a customer who wandered off.\n\n```python\nimport base64, concurrent.futures, pathlib\nfrom openai import OpenAI\nclient = OpenAI()\n\ndef gen_one(look_id: str, prompt: str, image_paths: list[pathlib.Path], out: pathlib.Path):\n    \"\"\"One gpt-image-2 image-edit call. Independent from the others.\"\"\"\n    result = client.images.edit(\n        model=\"gpt-image-2\",\n        image=[open(p, \"rb\") for p in image_paths],\n        prompt=prompt,\n        size=\"1024x1536\",\n        quality=\"high\",\n        n=1,\n    )\n    out.write_bytes(base64.b64decode(result.data[0].b64_json))\n    return look_id, out\n\n# Fire all looks concurrently. ThreadPoolExecutor is fine here — the work\n# is I/O-bound (waiting on OpenAI), GIL doesn't matter, and a small thread\n# pool keeps the rate-limit footprint modest.\nwith concurrent.futures.ThreadPoolExecutor(max_workers=len(LOOKS)) as ex:\n    futures = [ex.submit(gen_one, lk[\"id\"], lk[\"prompt\"], lk[\"images\"], lk[\"out\"])\n               for lk in LOOKS]\n    for fut in concurrent.futures.as_completed(futures):\n        look_id, out = fut.result()\n        print(f\"  ✓ {look_id} → {out}\")\n```\n\nBound the pool at the number of looks (typically 2–5) so you don't fan out beyond the actual work. OpenAI's per-user rate limits comfortably absorb a few concurrent image edits; you don't need a semaphore unless you're driving many customers from one key.\n\n**On failure of a single look**: catch + log inside `gen_one`; let the others finish. Premium-tier orchestration falls back per-look (Editorial for the failed one, Premium for the rest) rather than aborting the whole run.\n\n**Don't parallelize across customers from the same OpenAI key** — that's a different concern (rate limit fairness, billing, identity-cache hygiene). Each customer's lookbook gets its own thread pool scoped to that customer's looks; runs across customers stay sequential at the orchestrator level.\n\n## Prompt template\n\nThe prompt is **structured into five labeled blocks**, in this exact order. Each block is non-negotiable; never collapse them into prose because the model parses these labels as instructions. Always include the IDENTITY and GARMENT blocks even if they feel redundant — the model uses them to suppress its defaults.\n\n```\nIDENTITY — IMMUTABLE\nThe first <N> images are reference photos of the SAME person taken on different\ndays. Use ALL of them as identity anchors. The first image is the canonical\nface; the others provide additional angles, lighting, and build references.\nThe generated person MUST be recognizable as this exact individual.\n\nBuild:\n- Height: <height>\n- Weight: <weight>\n- Build: <build>\n- Shoulder width: <shoulder_width>\n- Torso/leg ratio: <torso_length> torso, <leg_length> legs\n- Posture: <posture>\n- Apparent age: <age_range>\n\nFace:\n- Hair: <hair_color>, <hair_style>\n- Beard: <beard>\n- Eyes: <eye_color>\n- Skin: <skin_tone>\n- Distinguishing features: <distinguishing_features>\n- Glasses: <glasses or \"none in this look\">\n\nHard constraints (do NOT violate):\n- Do NOT smooth skin into a generic model face. Preserve real skin texture and\n  asymmetry from the reference photos.\n- Do NOT change hair color, length, density, or part.\n- Do NOT change beard density, length, or shape.\n- Do NOT make the subject younger, leaner, more symmetrical, or more\n  conventionally attractive than the references show.\n- Body proportions, shoulder-to-waist ratio, and limb length must match the\n  references.\n- Do NOT produce a generic AI-male model face. If the generated face has\n  smooth-symmetric features, plastic skin, mannequin-like uniformity, an\n  unnaturally even jaw, or the \"conventionally photogenic\" look common in\n  image-gen output — that's the failure mode to avoid. The reference photos\n  are the source of truth; a generated face that \"looks better\" than the\n  reference is a wrong face.\n\nFace fidelity self-check (perform internally before emitting the output):\n1. Side-by-side compare the rendered face with reference image 1.\n2. Verify each: hair color + parting, beard pattern + density, eye color,\n   apparent age (NOT younger), skin tone, distinguishing features (scars,\n   moles, freckles, asymmetry).\n3. Verify facial asymmetry from the references (eye spacing, cheek structure,\n   jaw line, brow) is preserved — NOT smoothed to bilateral symmetry.\n4. If any check fails, regenerate the face region from the references before\n   emitting. A face that fails this check would be off-putting to the customer\n   (the lookbook is meant to look like THEM, not like a model who shares\n   their hair color).\n\nGARMENT — EXACT MATCH\nThere are <K> garment images that follow the identity references. Render each\nexactly as shown.\n\nGarment 1 — <slot, e.g. \"Top\">:\n- Name: <product name>\n- Color: <color name> (visual: <visual description, e.g. \"faded mid-blue, ~#6E8AA3, slightly cool undertone\">)\n- Fabric content: <e.g. \"60% cotton / 40% linen\">\n- Weight / hand: <e.g. \"~180 gsm, lightweight summer-weight, breathable\">\n- Weave / knit: <e.g. \"plain weave with slight slub texture\">\n- Drape: <e.g. \"soft, fluid, falls away from the body, light wrinkling at stress points\">\n- Silhouette / cut: <e.g. \"camp collar, short sleeve, single chest pocket, slightly curved hem, standard fit through chest and waist\">\n- Construction details: <e.g. \"natural shell buttons, single-needle topstitch, no contrast stitching\">\n- Fit on this body: size <size> on the build above; <how it sits — e.g. \"skims the chest, no bagginess at the waist, sleeve hits mid-bicep\">\n\nGarment 2 — <slot>:\n<same fields>\n\nHard constraints (do NOT violate):\n- Color must match the product flat-lay exactly. No re-tinting, no shifting toward warmer/cooler.\n- Fabric weight must read as described — a 180gsm linen does NOT look like a\n  300gsm canvas. Show appropriate drape and wrinkle behavior.\n- Do NOT add visible logos, brand wordmarks, contrast stitching, embellishments,\n  pocket flaps, or hardware that isn't in the flat-lay.\n- Do NOT change the silhouette to a tighter or looser fit than specified.\n\nSETTING\n<setting description from /mcp/lookbook/settings>\n\nCOMPOSITION\n<pose + framing — e.g. \"full-body, three-quarter turn, hands relaxed at sides,\neye-level 35mm, shallow depth of field\">\n\nSTYLE\nPhotorealistic 35mm editorial color photograph. Natural light. Shallow depth\nof field. Faithful skin tones (no Instagram filter look, no over-saturation).\nSubtle film grain. No text overlay. No brand marks. No watermarks.\n```\n\n### Variations for lookbook\n\nUse the same outfit + reference, vary the prompt's **setting** and **composition** lines across 3–5 calls:\n\n| Look # | Setting variation | Composition variation |\n|---|---|---|\n| 1 | Establishing shot of the venue | Wide, full-body, walking |\n| 2 | Closer environmental detail | Medium, three-quarter, looking off-camera |\n| 3 | Interior/transition setting | Seated or leaning, mid-conversation |\n| 4 | Golden-hour exterior | Backlit, contemplative |\n| 5 *(optional)* | Night/evening | Closer, warm interior light |\n\nKeep the outfit and customer identity consistent. Vary only the world.\n\n## Quality checks\n\nAfter each generation, eyeball:\n\n1. **Identity drift** — does the face still resemble the reference? If not, increase the weight of the reference by reordering it first and adding \"exact face from first image\" emphasis.\n2. **Garment fidelity** — color/silhouette match the product image? If not, name the color/material more concretely in the outfit line.\n3. **Logo invention** — `gpt-image-2` occasionally hallucinates wordmarks on tees/jackets. Add \"no text, no brand marks, no logos\" in the style line.\n4. **Anatomy** — extra fingers/limbs are still possible. If a generation has obvious flaws, regenerate once with the same prompt; don't iterate the prompt.\n\nIf 3 regenerations still fail on a look, fall back to **flat-lay** for that look and note it in the lookbook.\n\n## Face verification gate — second-line defense\n\nPrompt-level rules catch some identity drift but not all. Treat every Premium-tier generation as a candidate that has to pass an explicit verification gate **before** it gets stamped into `runs/<lookbook_id>/looks/` and consumed by `scripts/build-html-lookbook.py`. Without the gate, a single bad face can ship to the customer in a hosted lookbook — the trust-damaging failure this whole pipeline exists to avoid.\n\n### The gate\n\nImplementation: **`scripts/verify-face.py`**. Calls GPT-4o-vision with the generated PNG + the customer's reference photos and a strict rubric, returns JSON pass/fail.\n\nCost: ~$0.01–0.03 per generation (one vision call against ~3 images at 1024px). Compared to the $0.15–0.20 of the gpt-image-2 generation itself, the verification overhead is small change.\n\nUsage:\n\n```bash\npython3 scripts/verify-face.py \\\n  --generated runs/2026-05-09-mellow-la/looks/look1.png \\\n  --reference ~/Pictures/me-portrait.jpg ~/Pictures/me-fullbody.jpg \\\n  --threshold 6\n# Exit 0 = pass; exit 1 = fail (face drift); exit 2 = inconclusive (low-quality references, etc.)\n# Stdout: JSON {overall_pass, scores:{...}, off_putting, reason}\n```\n\n### Rubric (what the gate scores)\n\nThe vision call asks for a structured JSON response:\n\n```json\n{\n  \"hair_match\":      0-10,\n  \"beard_match\":     0-10,\n  \"eye_color_match\": 0-10,\n  \"skin_tone_match\": 0-10,\n  \"age_match\":       0-10,\n  \"asymmetry_match\": 0-10,\n  \"off_putting\":     0-10,   // higher = MORE generic-AI-face / uncanny\n  \"overall_pass\":    true | false,\n  \"reason\":          \"one sentence explaining the worst dimension\"\n}\n```\n\nDefault threshold (configurable via `--threshold`):\n- All match scores ≥ 6\n- `off_putting` ≤ 4\n- `overall_pass: true`\n\nThe gate is intentionally strict because the cost of shipping a bad face (customer sees an off-putting AI-face version of themselves) is higher than the cost of regenerating once or falling back to Editorial.\n\n### Recovery flow\n\nWhen `verify-face.py` fails (exit 1):\n\n1. **Retry once with a stronger prompt** — re-run gpt-image-2 with reference photo #1 moved to position 0 (highest weight) AND with the verifier's `reason` appended to the IDENTITY block as a directive (e.g., `reason: \"the rendered face has smoother skin and softer jaw than the reference\"` → append `\"Specifically: preserve the reference's skin texture and jaw definition; do NOT soften.\"`).\n2. **If retry also fails**, drop to Editorial tier for THAT look only. The other looks may still be Premium. Note the fallback in the run summary so the customer sees what happened (\"Look 02 fell back to Editorial — AI try-on couldn't match the customer's face after 2 attempts\").\n3. **Don't retry more than once.** A 3rd retry on the same look is throwing money at a model that's not cooperating; the right move is to fall back rather than chase a number.\n\nInconclusive (exit 2) is a different failure: low-quality references or the verifier itself failing. Surface it as a setup problem, don't auto-fall-through.\n\n### Where it fits in the pipeline\n\n- **Manual / interactive flow** (agent calls gpt-image-2 directly, drops PNGs into `runs/<id>/looks/`): the agent runs `verify-face.py` after each generation, before writing the `.lookbook_id` marker. Marker = \"this image is verified-canonical.\"\n- **Headless flow** (`scripts/run-headless-lookbook.py --tier premium --resume-build`): the orchestrator runs `verify-face.py` against every `look<N>.png` in `runs/<id>/looks/` automatically before consuming them. Failures emit a `❌ BLOCKER: face drift on look<N>` summary with the rubric scores; the agent can then regenerate that one look and re-resume.\n- **Optional gate**: invoke with `--no-verify` when the customer has explicitly opted out (e.g., they're iterating on prompts and want fast feedback). Default is verify-on.\n\n## Disclosure\n\nEvery customer-facing surface that includes a generated image must\n\nArchive v0.7.1: 48 files, 210267 bytes\n\nFiles: AGENTS.md (4951b), agents/openai.yaml (251b), CLAUDE.md (20294b), clawhub.json (5813b), docs/advanced/pima-api.md (14368b), examples/lookbook.md (8537b), examples/stock-check.md (3532b), PUBLISHING.md (6277b), README.md (9079b), references/acceptance-checklist.md (5150b), references/brand-style.md (16139b), references/cart-rules.md (4919b), references/event-suitability.md (6625b), references/headless-mode.md (17332b), references/hosting-options.md (22612b), references/image-generation.md (27438b), references/mcp-api.md (19570b), references/mpp.md (20100b), references/output-formats.md (41187b), references/run-layout.md (6359b), references/seasons.md (5105b), references/style-reasoning.md (7425b), references/voting.md (12550b), scripts/build-html-lookbook.py (26652b), scripts/deploy-lookbook.sh (11600b), scripts/discover-weekly-candidates.py (8214b), scripts/inject-voting-ui.py (10725b), scripts/lib/__init__.py (326b), scripts/lib/profile.py (3446b), scripts/run-headless-lookbook.py (34826b), scripts/score-calendar-event.py (6376b), scripts/validate-lookbook.py (10482b), scripts/verify-face.py (8741b), SECURITY.md (11590b), SKILL.md (37620b), templates/events.example.md (2070b), templates/profile.example.md (6680b), templates/profile.schema.json (10630b), templates/voting/functions-api-vote.js (2320b), templates/voting/functions-api-votes.js (1672b), templates/wardrobe.example.md (2104b), tests/conftest.py (484b), tests/test_build_html_lookbook.py (6894b), tests/test_profile.py (5363b), tests/test_score_calendar_event.py (4206b), tests/test_validate_lookbook.py (7693b), tests/test_verify_face.py (8932b), _meta.json (143b)\n\nArchive v0.6.5: 42 files, 190560 bytes\n\nFiles: CLAUDE.md (19461b), clawhub.json (5812b), docs/advanced/pima-api.md (14368b), examples/lookbook.md (8537b), examples/stock-check.md (3532b), PUBLISHING.md (6039b), README.md (7685b), references/acceptance-checklist.md (5150b), references/brand-style.md (16139b), references/cart-rules.md (4919b), references/event-suitability.md (6625b), references/headless-mode.md (16471b), references/hosting-options.md (22612b), references/image-generation.md (27438b), references/mcp-api.md (19570b), references/mpp.md (20100b), references/output-formats.md (40243b), references/run-layout.md (6359b), references/seasons.md (5105b), references/style-reasoning.md (7425b), scripts/build-html-lookbook.py (26652b), scripts/deploy-lookbook.sh (6645b), scripts/discover-weekly-candidates.py (8214b), scripts/lib/__init__.py (326b), scripts/lib/profile.py (3446b), scripts/run-headless-lookbook.py (33716b), scripts/score-calendar-event.py (6376b), scripts/validate-lookbook.py (10482b), scripts/verify-face.py (8741b), SECURITY.md (10808b), SKILL.md (34850b), templates/events.example.md (2070b), templates/profile.example.md (6344b), templates/profile.schema.json (10195b), templates/wardrobe.example.md (2104b), tests/conftest.py (484b), tests/test_build_html_lookbook.py (6894b), tests/test_profile.py (5212b), tests/test_score_calendar_event.py (4206b), tests/test_validate_lookbook.py (7693b), tests/test_verify_face.py (8932b), _meta.json (143b)\n\nArchive v0.6.4: 42 files, 189817 bytes\n\nFiles: CLAUDE.md (18073b), clawhub.json (5812b), docs/advanced/pima-api.md (14368b), examples/lookbook.md (8537b), examples/stock-check.md (3532b), PUBLISHING.md (6039b), README.md (7685b), references/acceptance-checklist.md (5150b), references/brand-style.md (16139b), references/cart-rules.md (4919b), references/event-suitability.md (6625b), references/headless-mode.md (16474b), references/hosting-options.md (22612b), references/image-generation.md (27438b), references/mcp-api.md (19570b), references/mpp.md (20100b), references/output-formats.md (40243b), references/run-layout.md (6359b), references/seasons.md (5105b), references/style-reasoning.md (7425b), scripts/build-html-lookbook.py (26652b), scripts/deploy-lookbook.sh (6645b), scripts/discover-weekly-candidates.py (8214b), scripts/lib/__init__.py (326b), scripts/lib/profile.py (3446b), scripts/run-headless-lookbook.py (33716b), scripts/score-calendar-event.py (6376b), scripts/validate-lookbook.py (10482b), scripts/verify-face.py (8741b), SECURITY.md (10808b), SKILL.md (34850b), templates/events.example.md (2070b), templates/profile.example.md (6344b), templates/profile.schema.json (10195b), templates/wardrobe.example.md (2104b), tests/conftest.py (484b), tests/test_build_html_lookbook.py (6894b), tests/test_profile.py (5212b), tests/test_score_calendar_event.py (4206b), tests/test_validate_lookbook.py (7555b), tests/test_verify_face.py (8974b), _meta.json (143b)\n\nArchive v0.6.2: 42 files, 187724 bytes\n\nFiles: CLAUDE.md (17348b), clawhub.json (3776b), docs/advanced/pima-api.md (14368b), examples/lookbook.md (8537b), examples/stock-check.md (3532b), PUBLISHING.md (6039b), README.md (7685b), references/acceptance-checklist.md (5150b), references/brand-style.md (16139b), references/cart-rules.md (4919b), references/event-suitability.md (6625b), references/headless-mode.md (16474b), references/hosting-options.md (22612b), references/image-generation.md (27438b), references/mcp-api.md (19570b), references/mpp.md (20100b), references/output-formats.md (40243b), references/run-layout.md (6359b), references/seasons.md (5105b), references/style-reasoning.md (7425b), scripts/build-html-lookbook.py (26315b), scripts/deploy-lookbook.sh (6645b), scripts/discover-weekly-candidates.py (8214b), scripts/lib/__init__.py (326b), scripts/lib/profile.py (3446b), scripts/run-headless-lookbook.py (33716b), scripts/score-calendar-event.py (6376b), scripts/validate-lookbook.py (10482b), scripts/verify-face.py (8741b), SECURITY.md (8031b), SKILL.md (34850b), templates/events.example.md (2070b), templates/profile.example.md (6344b), templates/profile.schema.json (10195b), templates/wardrobe.example.md (2104b), tests/conftest.py (484b), tests/test_build_html_lookbook.py (6894b), tests/test_profile.py (5212b), tests/test_score_calendar_event.py (4206b), tests/test_validate_lookbook.py (7555b), tests/test_verify_face.py (8974b), _meta.json (143b)\n\nArchive v0.6.1: 42 files, 187492 bytes\n\nFiles: CLAUDE.md (17348b), clawhub.json (3413b), docs/advanced/pima-api.md (14368b), examples/lookbook.md (8537b), examples/stock-check.md (3532b), PUBLISHING.md (6039b), README.md (7685b), references/acceptance-checklist.md (5150b), references/brand-style.md (16139b), references/cart-rules.md (4919b), references/event-suitability.md (6625b), references/headless-mode.md (16474b), references/hosting-options.md (22612b), references/image-generation.md (27438b), references/mcp-api.md (19570b), references/mpp.md (20100b), references/output-formats.md (40243b), references/run-layout.md (6359b), references/seasons.md (5105b), references/style-reasoning.md (7425b), scripts/build-html-lookbook.py (25959b), scripts/deploy-lookbook.sh (6645b), scripts/discover-weekly-candidates.py (8214b), scripts/lib/__init__.py (326b), scripts/lib/profile.py (3446b), scripts/run-headless-lookbook.py (33716b), scripts/score-calendar-event.py (6376b), scripts/validate-lookbook.py (10482b), scripts/verify-face.py (8741b), SECURITY.md (8031b), SKILL.md (34850b), templates/events.example.md (2070b), templates/profile.example.md (6344b), templates/profile.schema.json (10195b), templates/wardrobe.example.md (2104b), tests/conftest.py (484b), tests/test_build_html_lookbook.py (6894b), tests/test_profile.py (5212b), tests/test_score_calendar_event.py (4206b), tests/test_validate_lookbook.py (7555b), tests/test_verify_face.py (8974b), _meta.json (143b)\n\nArchive v0.6.0: 34 files, 174696 bytes\n\nFiles: CLAUDE.md (16504b), clawhub.json (3413b), docs/advanced/pima-api.md (14368b), examples/lookbook.md (8537b), examples/stock-check.md (3532b), PUBLISHING.md (6039b), README.md (7685b), references/acceptance-checklist.md (5150b), references/brand-style.md (16139b), references/cart-rules.md (4919b), references/event-suitability.md (6625b), references/headless-mode.md (16474b), references/hosting-options.md (22612b), references/image-generation.md (27438b), references/mcp-api.md (19570b), references/mpp.md (20100b), references/output-formats.md (40243b), references/run-layout.md (6359b), references/seasons.md (5105b), references/style-reasoning.md (7425b), scripts/build-html-lookbook.py (25959b), scripts...","readmeExcerpt":"Skill: Buck Mason Stylist Owner: nickmerwin Summary: Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lo... Tags: latest:0.7.2 Version history: v0.7.2 | 2026-05-19T16:51:28.017Z | auto Version 0.7.2 - Adds support and documentation for the agent-driven MPP checkout path via @stripe/link-cli. - Updates required ag","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"export OPENAI_API_KEY=sk-..."},{"language":"bash","snippet":"CLOUDFLARE_ACCOUNT_ID=<account-id> wrangler kv namespace create LOOKBOOK_VOTES"},{"language":"bash","snippet":"cp templates/profile.example.md ~/agent-workspace/profile.md\n$EDITOR ~/agent-workspace/profile.md   # fill in sizes, home zip, build, face, reference photos"},{"language":"text","snippet":"You:  Stock check on the Daily Shirt in olive, in my size near 90291.\nSkill: Online: 1,844 (in stock). Abbot Kinney: 36, Century City: 31. Pickup today.\n       https://www.buckmason.com/products/olive-daily-shirt"},{"language":"text","snippet":"You:  Build me a 3-look capsule for a Sonoma wedding in May, smart-casual.\nSkill: [pulls /mcp/buckmason/recommend, filters via style-reasoning matrix,\n        diffs against your wardrobe, generates 3 gpt-image-2 virtual try-on\n        images, outputs a hosted voting-enabled HTML lookbook or HTML-cart\n        handoff, then runs a fully-agent-driven MPP checkout if you've opted in]"},{"language":"bash","snippet":"clawhub install nickmerwin/buck-mason-stylist-skill\n\n# Some ClawHub install paths strip the executable bit on unpack — restore it\n# once after installing so the bundled scripts can run directly. (The skill's\n# own command examples all use explicit `bash` / `python3` prefixes that\n# work regardless, so this step is optional but tidier.)\nchmod +x ~/.clawhub/skills/buck-mason-stylist-skill/scripts/*"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: buck-mason-stylist\ndescription: Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lookbooks, and one-shot MPP checkout via link-cli. Customer brings sizes once; the agent reuses them across requests.\nversion: 0.7.2\nlicense: MIT\nauthors:\n  - Buck Mason / Pima\nruntime: any\ncompatibility:\n  mcp_servers: [pima-mcp]\n  binaries: [curl, jq]\nmetadata:\n  openclaw:\n    requires:\n      binaries: [curl, jq]\n      python: [python-pptx, Pillow]\n      optional_env: [OPENAI_API_KEY]\n      optional_binaries: [magick]\n      optional_clis: [\"@stripe/link-cli\"]\n    env_details:\n      OPENAI_API_KEY:\n        required: false\n        conditional: \"Default for any unqualified lookbook request: Premium-tier gpt-image-2 virtual try-on imagery, with Editorial/Minimum used only when Premium prerequisites are missing, fail, or the customer explicitly asks for no AI. Stock checks, recommend, MPP checkout, and order tracking are all unaffected by absence.\"\n        purpose: \"OpenAI /v1/images/edits with model gpt-image-2 for AI try-on imagery.\"\n        format: \"OpenAI API secret key, prefix sk-...\"\n        obtain_url: \"https://platform.openai.com/api-keys\"\n        notes: \"The OpenAI organization must be verified for gpt-image-2 access. Unverified orgs get 403 from /v1/images/edits; the skill surfaces that as an actionable error and offers Editorial tier rather than silently downgrading. See https://help.openai.com/en/articles/10910291.\"\n        how_to_set: \"export OPENAI_API_KEY=sk-...\"\n    optional_cli_details:\n      \"@stripe/link-cli\":\n        purpose: \"Required only for the fully-agent-driven MPP checkout path (workflow #4 step 3). Mints a one-time Stripe Shared Payment Token from the customer's Link wallet.\"\n        install: \"npm i -g @stripe/link-cli\"\n        publisher: \"Stripe (verified npm publisher)\"\n        source: \"https://github.com/stripe/link-cli\"\n        npm: \"https://www.npmjs.com/package/@stripe/link-cli\"\n        notes: \"Install only from the @stripe scope. Pin to a reviewed version in production. The skill never bundles or vendors this CLI.\"\n    categories: [commerce, image-generation, lookbook]\n    tags: [buck-mason, pima, stylist, shopping, stock, mcp]\n---\n\n# Buck Mason personal stylist\n\nYou are acting as a personal shopper for Buck Mason. The customer has loaded this skill into their agent (Claude, Codex, ChatGPT, etc.) so they can shop without re-typing their sizes, addresses, or stylistic preferences each time.\n\n> **What's loaded by agents at runtime, vs repo-only.** Follow refs from this `SKILL.md` only. Files like `README.md`, `PUBLISHING.md`, `SECURITY.md`, and `CLAUDE.md` exist for repo readers, ClawHub reviewers, and skill editors — agents should not load them at runtime. Anything an agent needs is either in the frontmatter, in `references/`, in `templates/`, in `scripts/`, or in `examples/`.\n\n## Environment\n\n**Premium try-on uses one opt"},{"path":"README.md","content":"# Buck Mason Stylist Skill\n\nA personal-shopping skill for [Buck Mason](https://www.buckmason.com), built for Claude Code, Codex, ChatGPT custom GPTs, and any agent that loads `SKILL.md`–style skills. Talks to the pima.io MCP at `pima.io/mcp/buckmason/*`.\n\n## What it does\n\n- **Stock check** — \"do they have the [item] in my size, online and at the Abbot Kinney store?\" — returns bucketed live counts (`In stock` / `Low stock (N left)` / `Out of stock`) per location.\n- **Wardrobe gap analysis** — \"what am I missing for a Sonoma wedding in May?\" — diffs your owned items against a season + climate + dress-code-aware capsule recommendation, with a one-sentence \"why\" per pick.\n- **AI try-on lookbooks** — by default, \"lookbook\" means gpt-image-2 virtual try-on images of you wearing the recommended outfits, assembled into a hosted HTML/HTML-cart lookbook with partner voting enabled.\n- **One-shot MPP checkout** — `POST /mcp/buckmason/checkout` speaks the [Merchant Payments Protocol](https://mpp.dev): HTTP 402 + Stripe Shared Payment Token via [`stripe/link-cli`](https://github.com/stripe/link-cli), push-approved by the customer in the Link app. See `references/mpp.md`.\n\n## Required setup\n\n### `OPENAI_API_KEY` (for the default AI try-on lookbook workflow)\n\n```bash\nexport OPENAI_API_KEY=sk-...\n```\n\nThe skill posts to `https://api.openai.com/v1/images/edits` with `model: \"gpt-image-2\"` to generate virtual try-on images. **The OpenAI organization tied to the key must be verified for `gpt-image-2`** (see <https://help.openai.com/en/articles/10910291>). Get a key at <https://platform.openai.com/api-keys>.\n\nThe other workflows — stock check, recommend, checkout, order tracking — do **not** require an OpenAI key. They only call the pima.io MCP. If a lookbook request is missing the key or usable reference photos, the skill should say what is missing and offer the Editorial tier instead of silently downgrading.\n\n### Voting on deployed lookbooks\n\nCloudflare Pages deploys include the voting mechanism by default. Create one KV namespace per Cloudflare account, then save the id in `profile.md` as `lookbook_votes_kv_id:` or export it as `LOOKBOOK_VOTES_KV_ID`.\n\n```bash\nCLOUDFLARE_ACCOUNT_ID=<account-id> wrangler kv namespace create LOOKBOOK_VOTES\n```\n\n### Profile\n\nOne profile file in your agent's workspace (copy from `templates/profile.example.md`):\n\n```bash\ncp templates/profile.example.md ~/agent-workspace/profile.md\n$EDITOR ~/agent-workspace/profile.md   # fill in sizes, home zip, build, face, reference photos\n```\n\nThat's it. No Buck Mason credentials. No Pima account.\n\nOptional but useful:\n- `wardrobe.md` — owned items (enables gap analysis)\n- `events.md` — upcoming travel/events (enables event-aware suggestions)\n- `magick`, `python-pptx`, `Pillow` — only needed for the PPT/HTML lookbook output (see `references/output-formats.md`)\n- [`stripe/link-cli`](https://github.com/stripe/link-cli) — required for the MPP fully-agent-driven checkout path\n\nFor MPP checkout, `profile"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn735q4jn33x2q1w5fpe46kqwd85wzq0\",\n  \"slug\": \"buck-mason-stylist-skill\",\n  \"version\": \"0.7.2\",\n  \"publishedAt\": 1779209488017\n}"},{"path":"references/acceptance-checklist.md","content":"# Lookbook acceptance checklist\n\nThe single source of truth for \"is this lookbook ready to share with the customer.\" Every `html` / `html-cart` / `ppt` lookbook clears these gates **before** the agent emits the URL or filename. The checklist is operationalized as `scripts/validate-lookbook.py` — run it; if it exits non-zero, fix the failure rather than narrating around it.\n\n## Local checks (run against the deploy directory before upload)\n\n| # | Check | Pass criterion |\n|---|---|---|\n| L1 | `index.html` exists | File present, ≥ 4 KB |\n| L2 | All product links absolute + on-brand | Every `<a href>` matching `/products/` resolves to `https://www.buckmason.com/products/<slug>` |\n| L3 | Prices present per piece | At least one `$\\d+` token within each `.piece` block |\n| L4 | Stock lines present per piece | Each `.piece` carries an `In stock` / `Low (N)` / `Out of stock` substring (the bucket strings from `/mcp/buckmason/stock`) |\n| L5 | AI try-on disclosure present (premium tier only) | Page contains \"AI-generated\" or \"AI try-on\" text, in cover or footer — required when any look hero came from gpt-image-2 |\n| L6 | OG meta tags present + absolute | `og:url`, `og:image`, `twitter:image` each resolve to URLs starting with `https://`, **not** containing `{{ABSOLUTE_PAGE_URL}}` / `{{ABSOLUTE_OG_IMAGE_URL}}` placeholders |\n| L7 | `og.jpg` present + correctly sized (multi-file deploys) | File exists in deploy dir, ~1200×630, `.jpg` mimetype, < 500 KB |\n| L8 | Per-piece checkboxes carry the full data set (`html-cart` only) | Every `.piece input[type=\"checkbox\"]` has `data-name`, `data-size`, `data-sku`, `data-qty`, `data-price-cents` |\n| L9 | Subtotals and look totals match piece prices | Per-look total equals the sum of piece prices in that look (rendered via JS only — verify the data attrs sum cleanly) |\n| L10 | No broken inline references | No `data-fullsize=\"\"`, no `<img src=\"\">`, no `href=\"#\"` placeholders |\n\n## Deployed checks (run after `wrangler pages deploy` against the live URL)\n\n| # | Check | Pass criterion |\n|---|---|---|\n| D1 | Page returns HTTP 200 | `curl -sI <page-url>` first line is `HTTP/2 200` (or `HTTP/1.1 200 OK`) |\n| D2 | `og.jpg` returns HTTP 200 + `image/jpeg` | `curl -sI <og-url>` shows `200` + `content-type: image/jpeg` |\n| D3 | Each `look<N>.jpg` returns 200 (multi-file deploys) | One per look section; if any 404s, redeploy with the missing asset |\n| D4 | Meta tags survived the deploy | The page body still contains all 13 OG/Twitter tags (no template substitution dropped them) |\n| D5 | Resolved URLs in OG metadata are reachable | The `og:url` and `og:image` URLs in the served HTML themselves return 200 (catches mismatched-alias bugs where the page deployed but the OG URL points at a stale/wrong subdomain) |\n| D6 | Unfurl preview works | Hit `https://www.opengraph.xyz/url/<encoded-url>` in a browser, or scrape with a `User-Agent: facebookexternalhit/1.1` and confirm the meta tags are visible |\n\n## Warnings (non-blocking, surface to th"},{"path":"references/brand-style.md","content":"# Buck Mason brand style guide\n\nA snapshot of the live storefront's visual language, extracted from `buckmason.com` directly (homepage + `/collections/curved-hem-tees` + `/products/white-slub-curved-hem-tee`) on **2026-05-05**. Use this when rendering any branded surface inside the skill — the `html-cart` lookbook in particular — so the output reads as \"by Buck Mason,\" not \"AI-generated for Buck Mason.\"\n\nFor higher-stakes artifacts the agent can optionally re-extract live (script in the appendix). Treat this file as a fast default; trust the live site if they diverge.\n\n## At a glance\n\n- **One typeface family does almost everything**: Adobe **Acumin Pro** (regular + condensed). Body text is regular Acumin Pro; every label, headline, button, and nav item is **Acumin Pro Condensed, UPPERCASE, with +2% letter-spacing**. There are no serifs anywhere on the standard catalog. Don't reach for Georgia, Canela, Söhne, or any \"editorial serif\" instinct — that's not how Buck Mason looks.\n- **The page is white.** `#FFFFFF` body, `#333333` text. Off-white `#F3F1EF` shows up only as an accent (header announcement banner). Don't paint the whole page off-white; that reads more J.Crew than Buck Mason.\n- **Imagery does the heavy lifting.** Headlines are small (~20px), tightly scaled, never bigger than the body's gallery photos. The brand voice is conveyed by hero photography and copy decks, not by display typography.\n- **Sharp corners.** `border-radius: 1px` (≈ 0) on CTAs, size selectors, and product tiles. Pills only on round badges (color swatches, etc., where `border-radius: 50%`).\n- **3:4 portrait product crops** (`0.75` aspect). Not 4:5, not 1:1, not 16:9.\n\n## Colors\n\n| Role | Value | Where it shows up |\n|---|---|---|\n| Page background | `#FFFFFF` (rgb 255,255,255) | `<html>`, `<body>`, `<main>` |\n| Primary text | `#333333` (rgb 51,51,51) | Body, headings, prices |\n| Active CTA | `#000000` on white text | \"Add to Bag\" once a size is selected (inferred from disabled state) |\n| Disabled CTA | `#AAAAAA` background, white text | \"Select Size to Add to Cart\" pre-size-pick |\n| Accent neutral | `#F3F1EF` (warm off-white) | Header announcement banner; subtle section panels |\n| Disabled-element text | `#D9D9D9` | Sold-out size selectors |\n| Border / hairline (when used) | `rgba(0,0,0,0.08)`-ish | Product tiles, form fields |\n\nThe site is otherwise **chromatic only through product imagery** — no brand reds/greens/blues. Stick to the white + black + grayscale stack and let the photos carry color.\n\n## Typography\n\n### Stacks\n\n```css\n/* Body */\nfont-family: acumin-pro, Helvetica, sans-serif;\n\n/* Headlines, labels, nav, buttons — every cap/eyebrow/CTA */\nfont-family: acumin-pro-condensed, Helvetica, sans-serif;\n```\n\nAcumin Pro / Acumin Pro Condensed are licensed via Adobe Fonts. If the rendered surface is a self-contained file (`html-cart`) and we deliberately don't `<link>` external font CDNs (per `references/output-formats.md`), fall back to **`Helvetica Neue Condensed`** "}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lo... Skill: Buck Mason Stylist Owner: nickmerwin Summary: Personal shopping skill for Buck Mason. Stock-checks (online + nearby store), wardrobe gap analysis, season- and event-aware outfit suggestions, AI try-on lo... Tags: latest:0.7.2 Version history: v0.7.2 | 2026-05-19T16:51:28.017Z | auto Version 0.7.2 - Adds support and documentation for the agent-driven MPP checkout path via @stripe/link-cli. - Updates required ag","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":2431,"uniquenessScore":46,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T19:57:04.367Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T19:57:04.367Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T23:47:32.860Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}