Create, validate, and improve ALPS profiles. Generate from natural language descriptions, validate existing profiles, and get improvement suggestions.
Generate, validate, and improve ALPS profiles for RESTful API design.
Goal: An ALPS that someone unfamiliar with the app can read and understand.
States = What the user sees
ProductList - viewing a list of productsProductDetail - viewing one productCart - viewing cart contentsTransitions = What the user does
goProductDetail - select a productdoAddToCart - add to cartSelf-documenting
title explains the purposedoc describes behavior and side effectsNo unreachable states
Necessary and sufficient
This skill responds to natural language requests:
How it works:
See "ALPS Surveyor Mode" section below for details.
When asked to analyze or improve an existing profile:
asd --validate <file> and check for ai-insights fieldasd --validate <file> to ensure no errors were introducedasd <file> -o <file>.html to create visual diagram for reviewnode -e "JSON.parse(require('fs').readFileSync('handover.json', 'utf8')); console.log('✓ Valid JSON')"
Schema: handover-protocol.jsonCRITICAL: Steps 6-9 are MANDATORY for every improvement session. Never skip validation of both ALPS profile and handover.json.
AI₁: Creates profile → Validates → Generates HTML → Creates handover.json
↓
AI₂: Reads handover.json → Identifies gaps → Improves → Validates → Generates HTML → Updates handover.json
↓
AI₃: Reads handover.json → Builds on previous work → Improves → Validates → Generates HTML → Updates handover.json
This creates a knowledge continuity where each AI builds on the work of previous AIs, just like developers reading code comments left by their predecessors.
Ontology - Semantic descriptors (data elements)
type="semantic" (default)id, title, and optionally doc and def (schema.org link)Taxonomy - State descriptors (screens/pages)
Choreography - Transition descriptors (actions)
type="safe" - Read operations (GET)type="unsafe" - Create operations (POST) - not idempotenttype="idempotent" - Update/Delete operations (PUT/DELETE)rt (return type) pointing to target state| Type | Prefix | Example |
|---|---|---|
| Safe transition | go |
goToHome, goProductList, goSearchProducts |
| Unsafe transition | do |
doCreateUser, doAddToCart, doLogin |
| Idempotent transition | do |
doUpdateUser, doDeleteItem, doRemoveFromCart |
| State/Page | PascalCase | HomePage, ProductDetail, ShoppingCart |
| Semantic field | camelCase | userId, productName, createdAt |
IMPORTANT: Safe transitions (go*) MUST include the target state name in their id.
rt="#ProductList" → id must be goProductList (or goToProductList)rt="#UserProfile" → id must be goUserProfile (or goToUserProfile)Invalid examples:
goStart with rt="#ProductList" - Wrong! Should be goProductListgoNext with rt="#Checkout" - Wrong! Should be goCheckoutThis rule ensures consistency and makes the diagram self-documenting. When a transition has no source state (entry point), it will be displayed as originating from UnknownState in the diagram.
Context clues for AI inference:
PUT (Update) indicators:
update, edit, modify, change, set, replacedoUpdateProfile, doEditComment, doSetQuantityDELETE indicators:
delete, remove, cancel, clear, destroydoDeleteUser, doRemoveFromCart, doCancelOrderFor complex, multi-sided platforms or large applications:
Domain Decomposition - Split into separate ALPS files by functional domain:
base.json - Core entities shared across domainscustomer-domain.json - Customer-facing featuresadmin-domain.json - Admin/management featuresseller-domain.json - Seller/provider features (for marketplaces)Design Each Domain Independently - Focus on one domain at a time with complete context
Merge Using asd merge - Combine domains iteratively:
# Merge customer domain into base
asd merge base.json customer-domain.json
# customer-domain.json now contains only conflicts (if any)
# Resolve conflicts in customer-domain.json, then re-merge
asd merge base.json customer-domain.json
# customer-domain.json is now empty (merge complete)
# Repeat for other domains
asd merge base.json admin-domain.json
Validate After Each Merge - Ensure no broken references or duplicate IDs
Benefits of this approach:
When a task exceeds token limits or requires multiple sessions (e.g., 200+ descriptors, multi-sided platforms), use the Handover Protocol (ADR 0006) to maintain continuity.
The Relay Race Metaphor:
Each AI session is a relay runner:
handover.json from previous sessionhandover.json for next sessionBasic handover.json structure:
{
"task": {
"type": "alps-generation",
"description": "E-commerce platform ALPS profile"
},
"current_state": {
"session_id": "ecommerce-003",
"total_sessions": 3,
"alps_profile": {
"total_descriptors": 180,
"validation_status": "valid"
}
},
"sessions": [
{
"session_id": "ecommerce-001",
"timestamp": "2025-12-17T10:00:00Z",
"handover_note": {
"summary": "Created ontology and customer domain (120 descriptors)",
"advice": "Admin domain needs careful planning - multiple user roles"
},
"descriptors_added": 120
},
{
"session_id": "ecommerce-002",
"timestamp": "2025-12-17T14:00:00Z",
"handover_note": {
"summary": "Added admin domain (60 descriptors). Validated successfully.",
"advice": "Seller domain remains. Consider splitting into sub-domains."
},
"descriptors_added": 60
}
],
"pending_work": {
"domains": ["seller-domain", "payment-flows"],
"notes": "Seller domain may need 80+ descriptors"
}
}
Key principles:
sessions array (never overwrite)handover.json syntax: node -e "JSON.parse(require('fs').readFileSync('handover.json', 'utf8'))"The Surveyor's Oath:
I am a relay runner in an endless race.
I carry the torch from my predecessor.
I run as far as I can.
I mark the map with what I learned.
I pass the torch to my successor.
I scatter.
The work continues.
For web surveying tasks (ALPS from website analysis), see the detailed ALPS Surveyor Mode section below.
IMPORTANT: Structure the ALPS file in three blocks in this order:
Identify Entities (Ontology - Semantic definitions)
def links to schema.org where applicabledoc for validation rules, formats, constraintsIdentify States (Taxonomy - Inclusion relationships)
doc explaining what user sees and available actionsIdentify Transitions (Choreography - State transitions)
go)do)do)doc explaining behavior, side effects, preconditionsAdd Documentation
titledoc when title alone cannot fully explain the descriptor:{"id": "title", "title": "Title", "doc": {"value": "Article title. Maximum 100 characters."}}{"id": "BlogPost", "doc": {"value": "User-created article. Visible to all users after publication."}}{"id": "doPublishBlogPost", "doc": {"value": "Publish article. Sets publishedAt to current time."}}def to link to schema.org definitions for standard conceptsdocAdd Tags for Organization
IMPORTANT: ALPS describes "what the user experiences" (application-level semantics), not "how it's implemented" (backend details).
Flow tags (PRIMARY): Group by user journey/experience with flow- prefix
flow-purchase, flow-hire, flow-consult, flow-cancel, flow-returnflow-cancel), not "cancellation batch processing"Domain tags (SECONDARY, optional): Group by technical domain with domain- prefix
domain-search, domain-cart, domain-payment, domain-analyticsStates and transitions should have both types where applicable
Tags are space-separated strings, not arrays
Example: "tag": "flow-purchase domain-cart" means "part of purchase journey, implemented in cart domain"
Add Semantic Descriptors to Transitions
{"id": "goProductDetail", "type": "safe", "rt": "#ProductDetail", "tag": "product", "descriptor": [
{"href": "#productId"}
]},
{"id": "doAddToCart", "type": "unsafe", "rt": "#Cart", "tag": "cart flow-purchase", "descriptor": [
{"href": "#productId"},
{"href": "#quantity"},
{"href": "#selectedVariant"}
]}
MANDATORY: Validate and Review Quality Metrics
asd --validate <file> to validate and get quality metricsGenerate Documentation
asd profile.json -o profile.html
Report Completion with Coverage Estimation
✅ Implementation Complete: [X] descriptors
📊 Coverage Estimation:
- [Domain 1]: [X]% ([reasoning])
- [Domain 2]: [X]% ([reasoning])
- Overall: [X]%
❌ Known Gaps:
- [Feature not implemented]
- [Area requiring more research]
File name: Always alps.json or alps.xml (fixed)
Directory:
alps/ directory exists → use it{app-name}/ directory (e.g., todo/, blog/, ecommerce/)Format selection:
Examples:
todo/alps.json
blog/alps.xml
ecommerce/alps.xml
Generate XML format by default. Use JSON only if explicitly requested.
XML Format (default):
<!-- Ontology -->, <!-- Taxonomy -->, <!-- Choreography --><?xml version="1.0" encoding="UTF-8"?>
<alps version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://alps-io.github.io/schemas/alps.xsd">
<title>Application Title</title>
<doc>Description of the application</doc>
<!-- Ontology -->
<descriptor id="fieldName" title="Human Title">
<doc>Description</doc>
</descriptor>
<descriptor id="otherField" title="Other Field"/>
<!-- Taxonomy -->
<descriptor id="StateName" title="State Title">
<descriptor href="#fieldName"/>
<descriptor href="#transitionName"/>
</descriptor>
<!-- Choreography -->
<descriptor id="goTargetState" type="safe" rt="#TargetState" title="Go to Target State"/>
<descriptor id="doAction" type="unsafe" rt="#ResultState" title="Perform Action">
<descriptor href="#requiredField"/>
</descriptor>
</alps>
JSON Format (when explicitly requested):
doc): Use multiple lines with "descriptor": [ at end of first line{
"$schema": "https://alps-io.github.io/schemas/alps.json",
"alps": {
"title": "Application Title",
"doc": {"value": "Description of the application"},
"descriptor": [
{"id": "fieldName", "title": "Human Title", "doc": {"value": "Description"}},
{"id": "otherField", "title": "Other Field"},
{"id": "StateName", "title": "State Title", "descriptor": [
{"href": "#fieldName"},
{"href": "#transitionName"}
]},
{"id": "goTargetState", "type": "safe", "rt": "#TargetState", "title": "Go to Target State"},
{"id": "doAction", "type": "unsafe", "rt": "#ResultState", "title": "Perform Action", "descriptor": [
{"href": "#requiredField"}
]}
]
}
}
Use asd --validate <file> to validate ALPS profiles and get quality metrics.
Output Format: JSON (pretty-printed, human-readable) Schema: validation-result.json
The validation output includes:
valid: Overall result (boolean)summary: Human-readable summary with emojierrors, warnings, suggestions: Validation issuesstatistics: Objective metrics (descriptor counts, coverage percentages)ai-insights: Subjective analysis by AI (only when AI runs validation)After generating ALPS, always validate with asd --validate. Key codes to watch for:
Errors (must fix):
Warnings (best practice):
Suggestions:
For detailed error descriptions and solutions, see Validation Reference.
Two complementary mechanisms help AI sessions build on previous work:
ai-insights (ADR 0005) - Analysis embedded in validation results:
asd --validate JSON outputhandover.json (ADR 0006) - State for ongoing multi-session tasks:
handover.json file in working directoryRelationship:
Single Session (Simple ALPS):
AI creates profile → Validates → ai-insights generated in validation output ✓
Multi-Session (Large ALPS):
Session 1: AI reads handover.json (if exists) → Works → Validates → Updates handover.json
Session 2: AI reads handover.json → Continues work → Validates → Updates handover.json
Session 3: AI reads handover.json → Completes → Validates → Final ai-insights in validation
Example workflow:
# Session 1: Start large ALPS project
asd base.json --validate # Creates ai-insights in validation result
# Create handover.json manually with initial task description
# Session 2: Continue work
# AI reads handover.json, adds customer domain
asd merge base.json customer-domain.json
asd base.json --validate # Check for errors
# AI updates handover.json with progress
# Session 3: Complete work
# AI reads handover.json, adds remaining domains
asd base.json --validate # Final validation with comprehensive ai-insights
# handover.json marks task complete
Both mechanisms create knowledge continuity, preventing each AI session from starting from scratch.
Input: "Create an ALPS for a simple blog with posts and comments"
Output (XML - default):
<?xml version="1.0" encoding="UTF-8"?>
<alps version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://alps-io.github.io/schemas/alps.xsd">
<title>Simple Blog</title>
<doc>ALPS profile for a blog application with posts and comments</doc>
<!-- Ontology -->
<descriptor id="postId" title="Post ID" def="https://schema.org/identifier">
<doc>Unique identifier for blog post</doc>
</descriptor>
<descriptor id="title" title="Post Title" def="https://schema.org/headline">
<doc>Article title. Maximum 100 characters.</doc>
</descriptor>
<descriptor id="body" title="Post Body" def="https://schema.org/articleBody">
<doc>Article content. Markdown format supported.</doc>
</descriptor>
<descriptor id="authorName" title="Author Name" def="https://schema.org/author"/>
<descriptor id="createdAt" title="Created Date" def="https://schema.org/dateCreated">
<doc>Publication date and time. ISO 8601 format.</doc>
</descriptor>
<descriptor id="commentId" title="Comment ID">
<doc>Unique identifier for comment</doc>
</descriptor>
<descriptor id="commentBody" title="Comment Text">
<doc>Comment content. Maximum 500 characters.</doc>
</descriptor>
<!-- Taxonomy -->
<descriptor id="Home" title="Home Page">
<doc>Blog home page. Shows navigation to post list.</doc>
<descriptor href="#goPostList"/>
</descriptor>
<descriptor id="PostList" title="Post List">
<doc>List of blog posts. Shows latest 10 posts with title and author.</doc>
<descriptor href="#postId"/>
<descriptor href="#title"/>
<descriptor href="#authorName"/>
<descriptor href="#goPostDetail"/>
<descriptor href="#goHome"/>
</descriptor>
<descriptor id="PostDetail" title="Post Detail">
<doc>Single post view. Shows full content and comments. Allows adding new comments.</doc>
<descriptor href="#postId"/>
<descriptor href="#title"/>
<descriptor href="#body"/>
<descriptor href="#authorName"/>
<descriptor href="#createdAt"/>
<descriptor href="#Comment"/>
<descriptor href="#goPostList"/>
<descriptor href="#doCreateComment"/>
</descriptor>
<descriptor id="Comment" title="Comment">
<doc>User comment on a post. Can be deleted by comment author or post author.</doc>
<descriptor href="#commentId"/>
<descriptor href="#commentBody"/>
<descriptor href="#authorName"/>
<descriptor href="#createdAt"/>
<descriptor href="#doDeleteComment"/>
</descriptor>
<!-- Choreography -->
<descriptor id="goHome" type="safe" rt="#Home" title="Go to Home">
<doc>Navigate to blog home page.</doc>
</descriptor>
<descriptor id="goPostList" type="safe" rt="#PostList" title="Go to Post List">
<doc>Display list of blog posts. Shows latest 10 posts.</doc>
</descriptor>
<descriptor id="goPostDetail" type="safe" rt="#PostDetail" title="Go to Post Detail">
<doc>Display full post content with comments.</doc>
<descriptor href="#postId"/>
</descriptor>
<descriptor id="doCreatePost" type="unsafe" rt="#PostDetail" title="Create Post">
<doc>Create new blog post. Post is immediately published.</doc>
<descriptor href="#title"/>
<descriptor href="#body"/>
</descriptor>
<descriptor id="doUpdatePost" type="idempotent" rt="#PostDetail" title="Update Post">
<doc>Update existing post content. Only post author can update.</doc>
<descriptor href="#postId"/>
<descriptor href="#title"/>
<descriptor href="#body"/>
</descriptor>
<descriptor id="doDeletePost" type="idempotent" rt="#PostList" title="Delete Post">
<doc>Delete post and all associated comments. Only post author can delete.</doc>
<descriptor href="#postId"/>
</descriptor>
<descriptor id="doCreateComment" type="unsafe" rt="#PostDetail" title="Add Comment">
<doc>Add comment to post. Comment is immediately visible.</doc>
<descriptor href="#postId"/>
<descriptor href="#commentBody"/>
</descriptor>
<descriptor id="doDeleteComment" type="idempotent" rt="#PostDetail" title="Delete Comment">
<doc>Delete comment. Comment author or post author can delete.</doc>
<descriptor href="#commentId"/>
</descriptor>
</alps>
Generated ALPS profiles can be visualized using app-state-diagram:
# Generate HTML documentation (default)
asd profile.json
# Generate SVG state diagram
asd profile.json -f svg
# Generate Mermaid classDiagram (GitHub/VSCode compatible)
asd profile.json -f mermaid
# Generate DOT format
asd profile.json -f dot
# Generate with watch mode
asd --watch profile.json
See llms.txt for CLI usage, programmatic API, and MCP server setup.
For simple descriptions, use plain text in doc.value. When you need structured content (lists, definitions, tables), use HTML format:
{"id": "doCheckout", "type": "unsafe", "rt": "#OrderConfirmation",
"title": "Complete Checkout",
"doc": {
"format": "html",
"value": "<dl><dt>Behavior</dt><dd>Processes payment, reserves inventory, sends confirmation email</dd><dt>Preconditions</dt><dd>Valid cart with items, payment method configured</dd><dt>Errors</dt><dd>Returns 400 if payment fails or items out of stock</dd></dl>"
}
}
Format support levels (per ALPS spec):
text: Required (default if not specified)html: Recommendedmarkdown: Optionalasciidoc: OptionalUse link elements to reference external documentation, schemas, or related resources:
{"id": "BlogPost", "def": "https://schema.org/BlogPosting",
"title": "Blog Post",
"doc": {"value": "User-created article visible to all after publication"},
"link": [
{"rel": "help", "href": "https://example.com/docs/blog-api.html", "title": "Blog API Documentation"},
{"rel": "related", "href": "https://example.com/schemas/post.json", "title": "JSON Schema"}
]
}
Link attributes:
rel (required): Relationship type - use IANA Link Relations (help, related, profile, etc.)href (required): URL to the related resourcetitle (optional): Human-readable description of the linktag (optional): Classification tagstag attribute to group related descriptorsThe alps-surveyor mode extracts ALPS profiles from existing websites by analyzing their structure. This is useful when:
Problem: Crawling every URL wastes tokens analyzing duplicate page types.
Solution: Three-layer strategy minimizes AI calls:
/products/{id}, /users/{username}/products/123, /products/456 → same pattern, only analyze onceUser: "Crawl https://www.bengo4.com and generate ALPS profile"
AI workflow:
1. Fetch homepage
2. Classify URLs: /lawyers/{id}, /area/{prefecture}/{city}, etc.
3. For each unique pattern:
- Fetch ONE example URL
- Extract DOM skeleton
- Call AI to generate ALPS descriptors
4. Merge all descriptors into unified ALPS profile
5. Validate and generate HTML diagram
6. Save progress to handover.json
The surveyor mode uses handover.json (per ADR 0006) to enable multi-session work. The handover uses a sessions array format to preserve historical context:
{
"$schema": "handover-protocol.json",
"task": {
"type": "alps-surveyor",
"target": "example.com ALPS profile"
},
"current_state": {
"session_id": "example-session-002",
"total_sessions": 2,
"alps_profile": {
"total_descriptors": 480,
"validation_status": "valid"
}
},
"sessions": [
{
"session_id": "example-session-001",
"timestamp": "2025-12-14T10:00:00Z",
"handover_note": {
"summary": "Initial crawl: search, consultation flows. 450 descriptors.",
"advice": "Bookmark feature needs attention - saw /bookmarks/* URLs"
},
"descriptors_added": 450
},
{
"session_id": "example-session-002",
"timestamp": "2025-12-14T14:00:00Z",
"handover_note": {
"summary": "Added bookmark and billing features using asd merge. Profile now at 480 descriptors.",
"advice": "Remaining: payment flows, mobile features, error states"
},
"descriptors_added": 30
}
],
"shared_context": {
"patterns_learned": {
"lawyer_profile": {"pattern": "/lawyers/{id}", "confidence": "high"}
}
},
"pending_work": {
"frontier_queue": [
"https://example.com/payment/checkout",
"https://example.com/dashboard"
]
},
"tools_available": {
"crawler": {"command": "node packages/crawler/test.mjs <url>"},
"merge": {"command": "asd merge <base> <source>"}
}
}
Key points:
sessions arraycurrent_state provides quick access to latest statusshared_context accumulates patterns learned across all sessionsmax_depth: 2 initially to testexclude_patterns to avoid login wallsfrontier_queue in handover.json to verify coverageasd --validate after each session to catch errors early✅ Core library implemented:
packages/app-state-diagram/src/crawler/url-pattern-classifier.tspackages/app-state-diagram/src/crawler/dom-skeleton-extractor.tspackages/app-state-diagram/src/crawler/alps-descriptor-generator.ts⏳ Integration in progress:
asd crawl)