zbeam-howto-spec

star 0

Generates HowTo JSON-LD spec from any page with a howToProceed block. Run after content-writer or page-updater. Produces a Copilot-ready SchemaFactory spec.

Air2air By Air2air schedule Updated 6/12/2026

name: zbeam-howto-spec description: "Generates HowTo JSON-LD spec from any page with a howToProceed block. Run after content-writer or page-updater. Produces a Copilot-ready SchemaFactory spec."

Z-Beam HowTo Spec Generator

Reads the howToProceed block from any Z-Beam page and produces two outputs:

  1. A validated HowTo JSON-LD spec (human-readable, for review)
  2. A Copilot implementation directive for SchemaFactory

This skill is reusable across all page types — application pages, location pages, and any future page type that carries a howToProceed block. It does not hardcode domain assumptions; it reads whatever structure is present and validates it.

Input required: slug of any page with a howToProceed block. Output: data/specs/howto-spec-[slug]-[YYYY-MM-DD].md


When to run

Run after zbeam-content-writer or zbeam-page-updater completes on any page. Specifically:

  • After any application page is written or updated (application pages require howToProceed)
  • After any location page that has a howToProceed block
  • When Copilot needs to implement or update SchemaFactory HowTo generation

Step 1: Read the page

import yaml, glob, json, os
from datetime import date

# Resolve page path — check applications, locations, then materials
slug = '[slug]'
candidates = [
    f'frontmatter/applications/{slug}.yaml',
    f'frontmatter/locations/{slug}.yaml',
    f'frontmatter/materials/{slug}.yaml',
]
page_path = next((p for p in candidates if os.path.exists(p)), None)
if not page_path:
    print(f'ERROR: No YAML found for slug {slug}')
    exit(1)

page = yaml.safe_load(open(page_path))
content_type = page.get('contentType', 'unknown')
howto = page.get('howToProceed')

if not howto:
    print(f'SKIP: {slug} has no howToProceed block — nothing to spec')
    exit(0)

print(f'Page: {slug} (contentType: {content_type})')
print(f'howToProceed title: {howto.get("title", "[missing]")}')
print(f'Steps found: {len(howto.get("steps", []))}')

Step 2: Validate the howToProceed block

Before generating the spec, validate the block can produce valid HowTo schema:

issues = []
steps = howto.get('steps', [])

# Title required
if not howto.get('title'):
    issues.append('FAIL: howToProceed.title missing')

# Steps: must be exactly 3
if len(steps) != 3:
    issues.append(f'FAIL: {len(steps)} steps found — HowTo requires exactly 3')

# Each step must be an object with name + text
for i, step in enumerate(steps):
    if isinstance(step, str):
        issues.append(f'FAIL: steps[{i}] is a plain string — must be object with name + text. '
                      f'Run zbeam-content-writer to rewrite the howToProceed block.')
    elif isinstance(step, dict):
        if not step.get('name'):
            issues.append(f'FAIL: steps[{i}].name missing')
        if not step.get('text'):
            issues.append(f'FAIL: steps[{i}].text missing')
        text_words = len(step.get('text', '').split())
        if text_words < 20:
            issues.append(f'WARN: steps[{i}].text is {text_words} words — HowTo steps under 20 words '
                          f'are unlikely to generate rich results')

if issues:
    print('Validation issues:')
    for issue in issues:
        print(f'  {issue}')
    if any(i.startswith('FAIL') for i in issues):
        print('\nBlocking failures found — fix howToProceed block before generating spec.')
        exit(1)
else:
    print('howToProceed block is valid — proceeding to spec generation.')

Step 3: Generate the HowTo JSON-LD spec

# Build the HowTo JSON-LD object
page_url = page.get('fullPath', f'/{content_type}s/{slug}')
base_url = 'https://www.z-beam.com'

howto_jsonld = {
    "@context": "https://schema.org",
    "@type": "HowTo",
    "name": howto.get('title', f'How to Proceed with {page.get("name", slug)}'),
    "description": howto.get('description', ''),
    "url": f"{base_url}{page_url}",
    "step": []
}

for i, step in enumerate(steps):
    howto_jsonld["step"].append({
        "@type": "HowToStep",
        "position": i + 1,
        "name": step.get('name', f'Step {i + 1}'),
        "text": step.get('text', '')
    })

# Optional: add totalTime if the page has duration data
# (skip for now — requires explicit duration field not yet in schema)

print(json.dumps(howto_jsonld, indent=2))

Step 4: Assess HowTo rich result eligibility

Google's criteria for HowTo rich results (as of 2026):

  • Step count: 2+ steps (we require 3 — compliant)
  • Each step: must have name and text (enforced in Step 2)
  • text per step: should be instructional, not promotional
  • Total: page must be publicly crawlable (not blocked by robots.txt)

Scan each step text for promotional language that disqualifies rich results:

promotional_signals = [
    'best in class', 'industry-leading', 'trusted by', 'guaranteed',
    'contact us today', 'call now', 'click here', 'learn more'
]

for i, step in enumerate(steps):
    text = step.get('text', '').lower()
    flagged = [p for p in promotional_signals if p in text]
    if flagged:
        print(f'WARN: steps[{i}] contains promotional language that may disqualify rich results: {flagged}')
        print(f'  Rewrite to be instructional (what to do + outcome) rather than promotional.')

Step 5: Write the Copilot SchemaFactory spec

Write a spec document Copilot can implement directly — no ambiguity, no guesswork:

os.makedirs('data/specs', exist_ok=True)
spec_path = f'data/specs/howto-spec-{slug}-{date.today()}.md'

spec_content = f"""# HowTo Schema Spec — {slug}
Generated: {date.today()}
Content type: {content_type}
Page URL: {base_url}{page_url}

## Task for Copilot

Implement HowTo JSON-LD generation in `SchemaFactory` for pages that have a
`howToProceed` block. This is a **new generator**, not a modification to an existing one.

### Files to modify

1. `app/utils/schema/SchemaFactory.ts` (or equivalent schema generation file)
2. Possibly `app/utils/schema/generators/HowToGenerator.ts` (new file, preferred)

### Implementation spec

**Trigger condition**: generate HowTo JSON-LD when `page.howToProceed` exists AND
`page.howToProceed.steps` is a non-empty array of objects with `name` and `text`.

Do NOT generate HowTo if `steps` is an array of plain strings — those are legacy format
and should not produce schema until the page is updated to the name+text format.

**Schema shape**:
```json
{json.dumps(howto_jsonld, indent=2)}

What to check before emitting:

function canEmitHowTo(page: PageData): boolean {{
  const howto = page.howToProceed;
  if (!howto?.steps?.length) return false;
  // Only emit if steps are objects (name+text), not plain strings
  return howto.steps.every(
    (step) => typeof step === 'object' && step.name && step.text
  );
}}

Where to inject the JSON-LD: Follow the existing pattern for other schema generators — add the HowTo block to the <script type="application/ld+json"> output for the page. The HowTo script tag is separate from the existing BusinessSchema / ServiceSchema tags (not merged into one tag).

FAQPage and Dataset schema removal: Both FAQPage and Dataset schema types have been deprecated from this project. Remove their generators from SchemaFactory entirely.

  • faq.items[] content remains in YAML and on the page — readers and AI citation systems still consume it. Only the JSON-LD generation is removed.
  • HowTo schema (from howToProceed) replaces FAQPage as the primary structured data signal on application pages.
  • Dataset schema is also removed — do not add Dataset generators elsewhere.
// REMOVE these generators from SchemaFactory:
// - FAQPageSchema / FAQPage generator
// - DatasetSchema / Dataset generator

// ADD this generator:
// - HowToSchema / HowTo generator (spec above)

Testing

After implementation, verify with:

npm run validate:rich-results  # existing rich results validator

Check that the HowTo JSON-LD appears in the page source for any application page with a valid howToProceed block, and does NOT appear for pages without one or with string-only steps.

Definition of done

  • HowTo JSON-LD emitted for {slug} at {base_url}{page_url}
  • HowTo not emitted for pages with string-only steps
  • HowTo and FAQPage coexist without errors
  • validate:rich-results passes
  • No TypeScript errors (npm run type-check) """

with open(spec_path, 'w') as f: f.write(spec_content)

print(f'Spec written to: {spec_path}')


---

## Step 6: Output summary

Report:
- Spec path
- HowTo step count and whether all pass rich result eligibility
- Validation issues found (if any)
- Next step: send spec to Copilot for SchemaFactory implementation

---

## What this skill does NOT do

- Does not modify the YAML page
- Does not implement SchemaFactory — it writes the spec for Copilot to implement
- Does not modify YAML page content — faq.items remain in the YAML file even though the JSON-LD FAQPage generator is deprecated
- Does not run if `howToProceed` is missing — use `zbeam-content-writer` first
- Does not handle plain-string steps — requires the name+text format enforced by the writer
Install via CLI
npx skills add https://github.com/Air2air/z-beam --skill zbeam-howto-spec
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator