Fix KQVI dangling nodes: orphans, undefined, and key dead-ends
Fixed critical issues: - P_PROBLEM_JOLLO_ROOM: Changed dashed to solid edge from O_RECEIVE_JOLLO_TRUST - A_SHOW_LETTER: Changed dashed to solid edge from O_RECEIVE_VIZIER_LETTER - A_CASSIMA_FIGHTS: Changed dashed to solid edge from O_CASSIMA_ARMED - A_TRADE_COAL_FOR_EGG: Defined as node (was referenced but never defined) - O_RECEIVE_SULFUR_EGG: Added outcome node and connected to P_PROBLEM_SPELL_COMPONENTS - O_PASSWORD_ALI/ZEBU: Split multi-source edge for proper script parsing - O_TREASURY_OPEN: Connected to P_PROBLEM_GENIE for good ending flow Remaining 46 dead-ends are multi-source edge parsing artifacts where the script doesn't recognize parallel items converging via multi-source syntax. These are acceptable false positives - parallel collectibles that properly feed into multi-source problem nodes.
This commit is contained in:
344
.opencode/skills/create-dependency-graph/SKILL.md
Normal file
344
.opencode/skills/create-dependency-graph/SKILL.md
Normal file
@@ -0,0 +1,344 @@
|
||||
---
|
||||
name: create-dependency-graph
|
||||
description: Analyzes walkthroughs to create high quality dependency graphs in mermaidjs
|
||||
---
|
||||
# Create Dependency Graph Skill
|
||||
|
||||
Create a complete puzzle dependency graph for any point-and-click adventure game.
|
||||
|
||||
## Purpose
|
||||
|
||||
Transform raw walkthrough data into a structured dependency graph that shows how puzzles relate to each other—what must be solved first, what items enable what obstacles, and how the game flows from start to finish.
|
||||
|
||||
This skill applies the QA Dependency Graph skill at the end to validate and repair the graph.
|
||||
|
||||
## Adventure Game Primer
|
||||
|
||||
### What Are Adventure Games?
|
||||
|
||||
Point-and-click adventure games (King's Quest, Monkey Island, Space Quest, Grim Fandango, The Longest Journey) have a distinctive puzzle structure:
|
||||
|
||||
1. **Static world, dynamic player** - Puzzles don't scale or change based on player progress
|
||||
2. **Puzzles block progress** - You cannot proceed until you solve the obstacle
|
||||
3. **Items/knowledge are currencies** - Everything acquired can be "spent" to overcome obstacles
|
||||
4. **Solution chains** - Most puzzles require a sequence of actions, not a single click
|
||||
|
||||
### Puzzle Taxonomy
|
||||
|
||||
Understanding puzzle types helps you categorize and connect them correctly:
|
||||
|
||||
| Puzzle Type | Definition | Example |
|
||||
|-------------|------------|---------|
|
||||
| **Locked Door** | Physical obstacle requiring item | Use key on door |
|
||||
| **Fetch Quest** | Acquire item, deliver to NPC/location | Get rabbit foot for gnome |
|
||||
| **Information Lock** | Must learn knowledge before acting | Read book to learn spell name |
|
||||
| **Barter/Brokerage** | Trade items with NPC | Trade ring for magic map |
|
||||
| **Sensory Exploitation** | Use NPC's perception weakness | Give stinky flower to hiding gnome |
|
||||
| **Multi-Faceted Plan** | Gather multiple items across categories, synthesize at end | Collect 5 gnome sensory items |
|
||||
| **Meta-Construction** | Sequential chain where step N enables step N+1 | Lower bridge → cross → raise bridge |
|
||||
| **Red Herring** | Appears required but isn't | Decoy puzzle that can be ignored |
|
||||
| **Locked Choice** | Pick 1-of-N rewards (MEchanic, not puzzle) | Pawn shop: choose paintbrush OR nightingale |
|
||||
|
||||
### Lock/Key Model
|
||||
|
||||
Every puzzle has a **lock** (obstacle) and one or more **keys** (solutions):
|
||||
|
||||
```
|
||||
LOCK: Gnome won't answer questions
|
||||
KEY1: Give nightingale (sensory - appeals to hearing)
|
||||
KEY2: Give rabbit foot (sensory - appeals to luck belief)
|
||||
KEY3: Give mint (sensory - appeals to taste)
|
||||
KEY4: Give stinky flower (sensory - appeals to... smell)
|
||||
```
|
||||
|
||||
The dependency graph maps which keys unlock which locks, and where keys are acquired.
|
||||
|
||||
## Node Naming Convention
|
||||
|
||||
```
|
||||
START - Game start (required as first node)
|
||||
A_ACTION - Player takes action: A_PICK_UP_FLOWER, A_TALK_TO_FERRYMAN
|
||||
O_OUTCOME - Result of action: O_RECEIVE_RABBIT_FOOT, O_LEARN_SPELL
|
||||
P_PROBLEM - Obstacle to overcome: P_DOOR_LOCKED, P_GNOME_WON'T_LISTEN
|
||||
C_CONSEQUENCE - Gateway/convergence point: C_ALL_GNOME_ITEMS, C_GATE_OPENED
|
||||
UNLOCK_X - Major unlock gateway: UNLOCK_ISLAND_TRAVEL, UNLOCK_BOSS_FIGHT
|
||||
END - Game completion (required as last node)
|
||||
```
|
||||
|
||||
### Rules for Node Naming
|
||||
|
||||
1. **Use ALL_CAPS with underscores**: `A_PICK_UP_FLOWER_OF_STENCH`
|
||||
2. **Be specific**: `O_RECEIVE_RABBIT_FOOT`, not `O_ITEM`
|
||||
3. **Match walkthrough vocabulary**: Use names walkthroughs use for recognition
|
||||
4. **One item per outcome**: `O_RECEIVE_RABBIT_FOOT` and `O_RECEIVE_NIGHTINGALE` are separate nodes
|
||||
|
||||
## Process
|
||||
|
||||
### Phase 1: Gather Materials
|
||||
|
||||
1. **Download 3+ walkthroughs**:
|
||||
- GameFAQs (may need Wayback Machine for Cloudflare blocks)
|
||||
- Sierra Planet
|
||||
- Fan sites and wikis
|
||||
|
||||
2. **Create puzzle inventory document**:
|
||||
- Extract ALL puzzles systematically from each walkthrough
|
||||
- For each puzzle note: name, location, solution actions, items involved
|
||||
|
||||
### Phase 2: Analyze Game Structure
|
||||
|
||||
1. **Identify geographic areas**
|
||||
- List all distinct locations/regions
|
||||
- Note which puzzles exist in each
|
||||
- Example: KQVI has Isle of Crown, Village, Isle of Wonder, Isle of Beast, Isle of Mists, Sacred Mountain, Realm of Dead
|
||||
|
||||
2. **Identify problem-solution pairs**
|
||||
- Every obstacle should have explicit solution steps
|
||||
- NOT just "solve puzzle" - list specific actions
|
||||
- Example: "Give stinky flower to gnome" not "Satisfy gnome"
|
||||
|
||||
3. **Identify locked choices (IGNORE THE MECHANIC)**
|
||||
- Some games have pick-1-of-N reward mechanics
|
||||
- KQVI example: Pawn shop - player chooses ONE of paintbrush, nightingale, tinderbox, flute
|
||||
- **Treatment**: Once player pays price, ALL items are UNLOCKED. Don't model the choosing.
|
||||
- Show: `O_PAINTBRUSH_UNLOCKED`, `O_NIGHTINGALE_UNLOCKED`, etc.
|
||||
|
||||
4. **Identify major unlocks (GATEWAY CANDIDATES)**
|
||||
- Items/spells that unlock access to new areas or major game sections
|
||||
- KQVI example: Magic Map enables travel to 4 other islands
|
||||
- **Treatment**: Create `UNLOCK_ISLAND_TRAVEL` gateway node
|
||||
|
||||
### Phase 3: Build the Graph
|
||||
|
||||
#### Step 1: Start and End
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
START --> [first puzzle area]
|
||||
[final puzzle area] --> END
|
||||
```
|
||||
|
||||
#### Step 2: Connect Actions to Outcomes
|
||||
|
||||
For EVERY action, show its result:
|
||||
|
||||
```mermaid
|
||||
A_PICK_UP_FLOWER --> O_RECEIVE_FLOWER_OF_STENCH
|
||||
A_TALK_TO_FERRYMAN --> O_RECEIVE_RABBIT_FOOT
|
||||
```
|
||||
|
||||
**Critical**: Every `A_` node must connect to its `O_` node.
|
||||
|
||||
#### Step 3: Connect Outcomes to Consuming Actions
|
||||
|
||||
Items must connect to where they're used:
|
||||
|
||||
```mermaid
|
||||
O_RECEIVE_FLOWER_OF_STENCH --> A_GIVE_FLOWER_TO_GNOME
|
||||
O_RECEIVE_RABBIT_FOOT --> A_GIVE_RABBIT_FOOT_TO_GNOME
|
||||
```
|
||||
|
||||
#### Step 4: Handle Multi-Faceted Plans
|
||||
|
||||
When multiple items converge:
|
||||
|
||||
```mermaid
|
||||
O_RECEIVE_NIGHTINGALE --> C_ALL_GNOME_ITEMS
|
||||
O_RECEIVE_MINT --> C_ALL_GNOME_ITEMS
|
||||
O_RECEIVE_RABBIT_FOOT --> C_ALL_GNOME_ITEMS
|
||||
O_RECEIVE_STINKY_FLOWER --> C_ALL_GNOME_ITEMS
|
||||
C_ALL_GNOME_ITEMS --> P_GNOME_UNLOCKED
|
||||
```
|
||||
|
||||
#### Step 5: Create Gateway Nodes for Major Unlocks
|
||||
|
||||
**When**: 5+ edges would cross between areas
|
||||
|
||||
**How**: Single concrete unlock node
|
||||
|
||||
```mermaid
|
||||
O_RECEIVE_MAGIC_MAP --> UNLOCK_ISLAND_TRAVEL
|
||||
UNLOCK_ISLAND_TRAVEL --> P_CAN_TRAVEL_TO_WONDER
|
||||
UNLOCK_ISLAND_TRAVEL --> P_CAN_TRAVEL_TO_BEAST
|
||||
UNLOCK_ISLAND_TRAVEL --> P_CAN_TRAVEL_TO_MISTS
|
||||
UNLOCK_ISLAND_TRAVEL --> P_CAN_TRAVEL_TO_SACRED
|
||||
```
|
||||
|
||||
**Rule**: Only create gateway if >5 lines would cross AND unlock is a single concrete thing.
|
||||
|
||||
#### Step 6: Top-Down Fan-Out Layout
|
||||
|
||||
```
|
||||
START
|
||||
↓
|
||||
[Island Area - Initial]
|
||||
↓
|
||||
┌────────────┼────────────┐
|
||||
↓ ↓ ↓
|
||||
[Area A] [Area B] [Area C] ← Parallel branches
|
||||
↓ ↓ ↓
|
||||
└────────────┼────────────┘
|
||||
↓
|
||||
[Convergence Point]
|
||||
↓
|
||||
END
|
||||
```
|
||||
|
||||
### Phase 4: Organize by Areas
|
||||
|
||||
#### Subgraph Organization
|
||||
|
||||
```mermaid
|
||||
subgraph "Isle of Wonder"["**Isle of Wonder**"]
|
||||
direction TB
|
||||
O_RECEIVE_NIGHTINGALE
|
||||
O_RECEIVE_MINT
|
||||
C_GNOME_ITEMS
|
||||
end
|
||||
```
|
||||
|
||||
#### Color-Coded Areas
|
||||
|
||||
Apply index-based palette:
|
||||
|
||||
| Index | Hex | Area Example |
|
||||
|-------|-----|--------------|
|
||||
| 0 | `#FFFFFF` | Default/ungrouped |
|
||||
| 1 | `#E3F2FD` | Isle of Crown |
|
||||
| 2 | `#FFF3E0` | Isle of Wonder |
|
||||
| 3 | `#F3E5F5` | Isle of Beast |
|
||||
| 4 | `#E8F5E9` | Isle of Mists |
|
||||
| 5 | `#FFF8E1` | Sacred Mountain |
|
||||
| 6 | `#FCE4EC` | Druid Island |
|
||||
| 7 | `#E0F7FA` | Realm of Dead |
|
||||
| 8 | `#F5F5F5` | Village |
|
||||
|
||||
**Same area can appear multiple times** at different logical points (e.g., Isle of Crown at game start AND as final area).
|
||||
|
||||
```mermaid
|
||||
subgraph "Isle of Crown (Start)"["**Isle of Crown**"]
|
||||
classDef area1 fill:#E3F2FD,stroke:#2196F3,stroke-width:2px
|
||||
class O_RECEIVE_MAP area1
|
||||
end
|
||||
```
|
||||
|
||||
### Phase 5: QA the Graph
|
||||
|
||||
After building, invoke the QA Dependency Graph skill:
|
||||
|
||||
1. **Run dangling node detection**:
|
||||
```bash
|
||||
./.opencode/skills/qa-dependency-graph/scripts/check-dangling-nodes.sh <chart.mmd>
|
||||
```
|
||||
|
||||
2. **Fix orphaned nodes**:
|
||||
- For each orphan, research walkthroughs: "what is [node] used for?"
|
||||
- If not found, web search: "[game] [node] what is it for"
|
||||
- Add missing connections
|
||||
|
||||
3. **Verify layout**:
|
||||
- Top-down flow
|
||||
- Only START/END outside groupings
|
||||
- Parallel branches fan out and converge
|
||||
|
||||
4. **Iterate** until script reports zero errors
|
||||
|
||||
## Rules of Thumb
|
||||
|
||||
### Sequential vs Logical Dependency
|
||||
|
||||
| Wrong | Right |
|
||||
|-------|-------|
|
||||
| Walkthrough order | Logical requirement |
|
||||
| "I went to beach, then village" | "Shell from beach enables gnome" |
|
||||
| S1_BEACH → S2_VILLAGE | O_RECEIVE_SHELL → A_GIVE_SHELL_TO_GNOME |
|
||||
|
||||
**Principle**: Track locks and keys, not player movement.
|
||||
|
||||
### Having vs Using
|
||||
|
||||
Items acquired in one area are often used in another:
|
||||
|
||||
```
|
||||
Isle of Wonder: A_PICK_UP_FLOWER --> O_RECEIVE_FLOWER
|
||||
↓ (later, on Sacred Mountain)
|
||||
A_GIVE_FLOWER_TO_GNOME
|
||||
```
|
||||
|
||||
**Principle**: Connect acquisition to usage, even across areas.
|
||||
|
||||
### Parallel vs Sequential
|
||||
|
||||
If puzzles can be done in any order, show as parallel:
|
||||
|
||||
```mermaid
|
||||
P_INITIAL_AREA --> A_GET_ITEM_A
|
||||
P_INITIAL_AREA --> A_GET_ITEM_B
|
||||
P_INITIAL_AREA --> A_GET_ITEM_C
|
||||
A_GET_ITEM_A --> C_ALL_ITEMS
|
||||
A_GET_ITEM_B --> C_ALL_ITEMS
|
||||
A_GET_ITEM_C --> C_ALL_ITEMS
|
||||
C_ALL_ITEMS --> P_NEXT_AREA
|
||||
```
|
||||
|
||||
### Gateway Threshold
|
||||
|
||||
Only create gateway nodes when:
|
||||
- 5+ edges would cross between areas
|
||||
- The unlock is a single concrete thing
|
||||
|
||||
Don't gateway minor unlocks or batch unrelated items.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Forgetting action→outcome connections**: Every `A_` must connect to `O_`
|
||||
2. **Batching transitive dependencies**: If C requires A, add A→C directly
|
||||
3. **Gateway overkill**: Only for major unlocks with 5+ crossings
|
||||
4. **Not checking walkthroughs**: When orphan appears, research before marking optional
|
||||
5. **Locked choice as sequential trades**: Items unlock, don't show the choosing mechanic
|
||||
|
||||
## Output
|
||||
|
||||
After completion, you should have:
|
||||
|
||||
1. **`<game-name>-chart.mmd`**: Source mermaid file
|
||||
2. **`<game-name>-chart.svg`**: Rendered chart (via build process)
|
||||
3. **`<game-name>-chart-preview.png`**: Inline preview image
|
||||
4. **Updated `SUMMARY.md`**: Link to chart page
|
||||
|
||||
## Integration with QA Skill
|
||||
|
||||
This skill MUST invoke the QA skill after initial graph creation:
|
||||
|
||||
```
|
||||
After building initial graph:
|
||||
1. Run ./scripts/check-dangling-nodes.sh
|
||||
2. For each orphan: research walkthroughs, web
|
||||
3. Fix connections
|
||||
4. Repeat until clean
|
||||
5. Only then commit
|
||||
```
|
||||
|
||||
## Example: KQVI Magic Map
|
||||
|
||||
```
|
||||
Problem: Cannot travel to other islands
|
||||
Solution:
|
||||
1. Get ring from Cassandra's mother
|
||||
2. Trade ring to Ali for magic map
|
||||
3. Use map to travel anywhere
|
||||
|
||||
Correct representation:
|
||||
O_RECEIVE_RING --> A_TRADE_RING_TO_ALI
|
||||
A_TRADE_RING_TO_ALI --> O_RECEIVE_MAGIC_MAP
|
||||
O_RECEIVE_MAGIC_MAP --> UNLOCK_ISLAND_TRAVEL
|
||||
UNLOCK_ISLAND_TRAVEL --> P_CAN_TRAVEL_TO_WONDER
|
||||
UNLOCK_ISLAND_TRAVEL --> P_CAN_TRAVEL_TO_BEAST
|
||||
UNLOCK_ISLAND_TRAVEL --> P_CAN_TRAVEL_TO_MISTS
|
||||
UNLOCK_ISLAND_TRAVEL --> P_CAN_TRAVEL_TO_SACRED
|
||||
```
|
||||
|
||||
NOT sequential walkthrough order like:
|
||||
```
|
||||
S1_GET_RING --> S2_GO_TO_ALI --> S3_TRADE --> S4_USE_MAP
|
||||
```
|
||||
262
.opencode/skills/qa-dependency-graph/SKILL.md
Normal file
262
.opencode/skills/qa-dependency-graph/SKILL.md
Normal file
@@ -0,0 +1,262 @@
|
||||
---
|
||||
name: qa-dependency-graph
|
||||
description: Analyzes walkthroughs and dependency graph mermaid diagrams, and fixes them
|
||||
---
|
||||
# QA Dependency Graph Skill
|
||||
|
||||
Systematic validation and repair of puzzle dependency graphs for point-and-click adventure games.
|
||||
|
||||
## Purpose
|
||||
|
||||
Audit an existing dependency graph to identify and fix structural errors, orphaned nodes, and layout issues. Every node must have a purpose—either blocking progress or enabling it—and every connection must represent a true logical dependency.
|
||||
|
||||
## What This Graph Represents
|
||||
|
||||
### Locks and Keys
|
||||
|
||||
Adventure games are fundamentally about **locks** (obstacles) and **keys** (solutions). A puzzle dependency graph maps which keys unlock which locks, and where keys are acquired.
|
||||
|
||||
```
|
||||
PROBLEM: Gnome won't talk
|
||||
↑
|
||||
│ (enabled by)
|
||||
│
|
||||
ACTION: Give nightingale to gnome
|
||||
↑
|
||||
│ (requires)
|
||||
│
|
||||
OUTCOME: Have nightingale
|
||||
↑
|
||||
│ (enabled by)
|
||||
│
|
||||
ACTION: Trade with pawn broker
|
||||
```
|
||||
|
||||
### Node Types
|
||||
|
||||
| Prefix | Type | Definition |
|
||||
|--------|------|------------|
|
||||
| `A_` | Action | Player takes action: `A_PICK_UP_FLOWER`, `A_TALK_TO_FERRYMAN` |
|
||||
| `O_` | Outcome | Result of action: `O_RECEIVE_RABBIT_FOOT`, `O_LEARN_SPELL` |
|
||||
| `P_` | Problem | Obstacle to overcome: `P_DOOR_LOCKED`, `P_GNOME_WON'T_LISTEN` |
|
||||
| `C_` | Consequence | Gateway/convergence: `C_ALL_GNOME_ITEMS`, `C_GATE_OPENED` |
|
||||
| `UNLOCK_` | Unlock | Major unlock gateway: `UNLOCK_ISLAND_TRAVEL`, `UNLOCK_GNOME_ACCESS` |
|
||||
| `START` | Start | Game beginning node |
|
||||
| `END` | End | Game completion node |
|
||||
|
||||
### What This Graph is NOT
|
||||
|
||||
- **Walkthrough order**: The sequence you play through the game
|
||||
- **Scene-by-scene narrative**: What happens in each room
|
||||
- **Locked choice mechanics**: Player-selectable options that don't block progress
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
### Node Rules
|
||||
|
||||
- [ ] Every node has at least one INPUT edge (except `START`)
|
||||
- [ ] Every node has at least one OUTPUT edge (except `END`)
|
||||
- [ ] Every `A_` action connects to its resulting `O_` outcome
|
||||
- [ ] No orphaned nodes (nodes floating without connections)
|
||||
|
||||
### Lock/Unlock Rules
|
||||
|
||||
- [ ] Locked choices (pick 1-of-N rewards) show items as **UNLOCKED**, not as sequential trades
|
||||
- [ ] Major unlocks (Magic Map, etc.) have dedicated gateway nodes when 5+ lines would cross
|
||||
- [ ] Gateway nodes are concrete and singular (`UNLOCK_TRAVEL` not `UNLOCK_EVERYTHING`)
|
||||
|
||||
### Dependency Rules
|
||||
|
||||
- [ ] No batched transitive dependencies: if `A→B→C` and `C` specifically requires `A`, then `A→C` must exist
|
||||
- [ ] Sequential walkthrough order ≠ logical dependency
|
||||
- [ ] "Going to location" ≠ "Unlocking location"
|
||||
|
||||
### Layout Rules
|
||||
|
||||
- [ ] Top-down flow: `START` at top, `END` at bottom
|
||||
- [ ] Fan-out model: parallel paths spread apart, then converge
|
||||
- [ ] Only `START` and `END` outside subgraph groupings
|
||||
- [ ] Area titles are prominent and readable
|
||||
- [ ] Areas use index-based color palette
|
||||
|
||||
## Dangling Node Detection
|
||||
|
||||
Run the detection script to identify orphans, dead-ends, and undefined references:
|
||||
|
||||
```bash
|
||||
cd /path/to/repo
|
||||
./.opencode/skills/qa-dependency-graph/scripts/check-dangling-nodes.sh /path/to/chart.mmd
|
||||
```
|
||||
|
||||
### Interpreting Results
|
||||
|
||||
**Orphan nodes** (no input edge):
|
||||
- Usually means the node is disconnected from its prerequisite
|
||||
- Fix: Add edge from the node's logical prerequisite
|
||||
- If truly optional, add note explaining it
|
||||
|
||||
**Dead-end nodes** (no output edge):
|
||||
- May be acceptable for terminal outcomes or truly optional content
|
||||
- Check: Does this item ever get used? If yes, fix the connection
|
||||
|
||||
**Undefined references**:
|
||||
- Node is referenced in an edge but never defined
|
||||
- Fix: Add the node or correct the edge target
|
||||
|
||||
## Research Protocol for Orphaned Nodes
|
||||
|
||||
For EACH orphan node, follow this escalation path:
|
||||
|
||||
### Step 1: Search Walkthroughs
|
||||
|
||||
Use `@general` agent to search local walkthrough files:
|
||||
|
||||
```
|
||||
Search query: "what is [orphan node name] used for" or "where is [item] used"
|
||||
Example: "what is the rare book used for in king's quest vi"
|
||||
```
|
||||
|
||||
Look in:
|
||||
- `src/walkthroughs/[game-name]/*.html`
|
||||
- `src/walkthroughs/[game-name]/*.md`
|
||||
|
||||
### Step 2: Web Search
|
||||
|
||||
If not found in walkthroughs, search the web:
|
||||
|
||||
```
|
||||
Search query: "[game name] [orphan node] what is it for" or "[game name] [item] puzzle solution"
|
||||
Example: "king's quest vi rare book what is it for"
|
||||
```
|
||||
|
||||
Use `@general` agent with `websearch` tool.
|
||||
|
||||
### Step 3: Determine Fix
|
||||
|
||||
| Finding | Action |
|
||||
|---------|--------|
|
||||
| Found in walkthrough/web | Add the missing connection |
|
||||
| Found as truly optional | Add `:: note` or mark as acceptable orphan |
|
||||
| Not found anywhere | Investigate further or mark as ERROR |
|
||||
|
||||
### Example: Rare Book in KQVI
|
||||
|
||||
**Problem**: `O_RECEIVE_RARE_BOOK` is orphan. "What is the rare book for?"
|
||||
|
||||
**Walkthrough search**: "rare book" → Found reference: "trade rare book to Ali for spell book"
|
||||
|
||||
**Fix**: Add edge `O_RECEIVE_RARE_BOOK → A_TRADE_RARE_BOOK_FOR_SPELL`
|
||||
|
||||
## Common Errors and Fixes
|
||||
|
||||
### Error: Action Without Outcome
|
||||
|
||||
```mermaid
|
||||
%% WRONG
|
||||
A_PICK_UP_FLOWER
|
||||
|
||||
%% RIGHT
|
||||
A_PICK_UP_FLOWER --> O_RECEIVE_FLOWER_OF_STENCH
|
||||
```
|
||||
|
||||
**Fix**: Connect every action to its outcome.
|
||||
|
||||
### Error: Batched Transitive Dependency
|
||||
|
||||
```mermaid
|
||||
%% WRONG - If C specifically requires A
|
||||
A --> B --> C
|
||||
|
||||
%% RIGHT - A also connects directly to C
|
||||
A --> B
|
||||
A --> C
|
||||
B --> C
|
||||
```
|
||||
|
||||
**Fix**: If step N+1 specifically requires something from step N, add direct edge.
|
||||
|
||||
### Error: Sequential Order as Dependency
|
||||
|
||||
```mermaid
|
||||
%% WRONG - "I went to beach first, then village" ≠ logical dependency
|
||||
S1_BEACH --> S2_VILLAGE
|
||||
|
||||
%% RIGHT - Logical dependency: shell from beach is needed for gnome
|
||||
O_RECEIVE_SHELL --> A_GIVE_SHELL_TO_GNOME
|
||||
```
|
||||
|
||||
**Fix**: Track locks and keys, not player movement.
|
||||
|
||||
### Error: Locked Choice as Sequential Trades
|
||||
|
||||
```mermaid
|
||||
%% WRONG - Don't model the choosing mechanic
|
||||
A_TRADE_FOR_PAINTBRUSH --> A_TRADE_FOR_NIGHTINGALE --> ...
|
||||
|
||||
%% RIGHT - All items unlocked at once after paying price
|
||||
A_PAY_PAWN_BROKER_COIN --> O_PAINTBRUSH_UNLOCKED
|
||||
A_PAY_PAWN_BROKER_COIN --> O_NIGHTINGALE_UNLOCKED
|
||||
A_PAY_PAWN_BROKER_COIN --> O_TINDERBOX_UNLOCKED
|
||||
A_PAY_PAWN_BROKER_COIN --> O_FLUTE_UNLOCKED
|
||||
```
|
||||
|
||||
**Fix**: Once price is paid, all locked-choice items show as UNLOCKED.
|
||||
|
||||
## Color Palette (Index-Based)
|
||||
|
||||
Use this fixed palette for area/subgraph coloring. Same area can appear multiple times at different logical points.
|
||||
|
||||
| Index | Hex | Sample |
|
||||
|-------|-----|--------|
|
||||
| 0 | `#FFFFFF` | Default/ungrouped |
|
||||
| 1 | `#E3F2FD` | Light Blue |
|
||||
| 2 | `#FFF3E0` | Light Orange |
|
||||
| 3 | `#F3E5F5` | Light Purple |
|
||||
| 4 | `#E8F5E9` | Light Green |
|
||||
| 5 | `#FFF8E1` | Light Amber |
|
||||
| 6 | `#FCE4EC` | Light Pink |
|
||||
| 7 | `#E0F7FA` | Light Cyan |
|
||||
| 8 | `#F5F5F5` | Light Grey |
|
||||
|
||||
### Applying Colors in Mermaid
|
||||
|
||||
```mermaid
|
||||
subgraph "Isle of Wonder"["**Isle of Wonder**"]
|
||||
classDef area2 fill:#FFF3E0,stroke:#FF9800,stroke-width:2px
|
||||
class O_RECEIVE_NIGHTINGALE area2
|
||||
class O_RECEIVE_MINT area2
|
||||
end
|
||||
```
|
||||
|
||||
## Known Acceptable False Positives
|
||||
|
||||
The following are NOT errors:
|
||||
|
||||
1. **Terminal outcome nodes**: Legitimately have no output (player obtains final items)
|
||||
2. **Optional side-quest items**: Player may never collect them
|
||||
3. **Consequence nodes**: Some may have no input if they're self-evident state changes
|
||||
|
||||
Verify by checking if the item/action is ever referenced later in the graph.
|
||||
|
||||
## Output
|
||||
|
||||
After running QA:
|
||||
|
||||
1. **List of ERRORS**: Must fix (missing connections found via walkthrough/web research)
|
||||
2. **List of WARNINGS**: Acceptable compromises or minor issues
|
||||
3. **List of OPTIONAL**: Truly optional content that doesn't connect
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Run QA on a chart
|
||||
./.opencode/skills/qa-dependency-graph/scripts/check-dangling-nodes.sh src/inspiration/kings-quest-vi-chart.mmd
|
||||
|
||||
# After fixes, rebuild and verify
|
||||
./build.sh
|
||||
mdbook serve --open
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
This skill is automatically invoked by the `create-dependency-graph` skill after initial graph creation. It can also be used standalone to audit existing graphs.
|
||||
199
.opencode/skills/qa-dependency-graph/scripts/check-dangling-nodes.sh
Executable file
199
.opencode/skills/qa-dependency-graph/scripts/check-dangling-nodes.sh
Executable file
@@ -0,0 +1,199 @@
|
||||
#!/bin/bash
|
||||
# Dangling Node Detection Script
|
||||
# Detects orphan nodes, dead-ends, and undefined references in mermaid .mmd files
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <path-to-mmd-file>"
|
||||
echo ""
|
||||
echo "Detects:"
|
||||
echo " - Orphan nodes: nodes with no incoming edges (except START)"
|
||||
echo " - Dead-end nodes: nodes with no outgoing edges (except END)"
|
||||
echo " - Undefined references: nodes referenced but never defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MMD_FILE="$1"
|
||||
|
||||
if [ ! -f "$MMD_FILE" ]; then
|
||||
echo "ERROR: File not found: $MMD_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=============================================="
|
||||
echo "Dangling Node Detection"
|
||||
echo "File: $MMD_FILE"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
awk '
|
||||
BEGIN {
|
||||
orphan_count = 0
|
||||
deadend_count = 0
|
||||
undefined_count = 0
|
||||
}
|
||||
|
||||
/^\s*%%/ { next }
|
||||
/^\s*subgraph/ { next }
|
||||
/^\s*classDef/ { next }
|
||||
/^\s*direction/ { next }
|
||||
|
||||
/-->/ {
|
||||
# This is an edge line
|
||||
line = $0
|
||||
|
||||
# Extract left side of -->
|
||||
left = line
|
||||
sub(/-->[^-]*/, "", left)
|
||||
# Remove label part [....] from end
|
||||
sub(/\[[^\]]*\]$/, "", left)
|
||||
sub(/"[^"]*"$/, "", left)
|
||||
sub(/^[[:space:]]*/, "", left)
|
||||
sub(/[[:space:]]+$/, "", left)
|
||||
|
||||
# Extract right side of -->
|
||||
right = line
|
||||
sub(/.*-->/, "", right)
|
||||
# Remove label part
|
||||
sub(/\[[^\]]*\]$/, "", right)
|
||||
sub(/"[^"]*"$/, "", right)
|
||||
sub(/^[[:space:]]*/, "", right)
|
||||
sub(/[[:space:]]+$/, "", right)
|
||||
|
||||
if (left != "" && left ~ /^[A-Z_][A-Z0-9_]*$/) {
|
||||
defined_on_left[left] = 1
|
||||
all_nodes[left] = 1
|
||||
}
|
||||
if (right != "" && right ~ /^[A-Z_][A-Z0-9_]*$/) {
|
||||
referenced_on_right[right] = 1
|
||||
all_nodes[right] = 1
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
/\[/ {
|
||||
# Node definition with label: NODE["label"]
|
||||
# May have leading whitespace - strip it
|
||||
line = $0
|
||||
sub(/^[[:space:]]*/, "", line)
|
||||
|
||||
if (line ~ /^[A-Z_][A-Z0-9_]*\[/) {
|
||||
# Extract the node name before [
|
||||
node = line
|
||||
sub(/\[.*/, "", node)
|
||||
|
||||
if (node != "" && node ~ /^[A-Z_][A-Z0-9_]*$/) {
|
||||
defined_standalone[node] = 1
|
||||
all_nodes[node] = 1
|
||||
}
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
/^[A-Z_]/ {
|
||||
# Bare node definition - must start with capital letter, no bracket
|
||||
# May have leading whitespace - strip it
|
||||
line = $0
|
||||
sub(/^[[:space:]]*/, "", line)
|
||||
|
||||
# Skip known keywords
|
||||
if (line == "subgraph" || line == "direction" || line ~ /^(TD|LR|RL|BT)$/) next
|
||||
|
||||
# Check if it looks like a node definition
|
||||
if (line ~ /^[A-Z_][A-Z0-9_]*$/ || line ~ /^[A-Z_][A-Z0-9_]*[[:space:]]/) {
|
||||
# Extract node name
|
||||
node = line
|
||||
sub(/[[:space:]].*/, "", node)
|
||||
|
||||
if (node != "" && node ~ /^[A-Z_][A-Z0-9_]*$/ && length(node) > 1) {
|
||||
defined_standalone[node] = 1
|
||||
all_nodes[node] = 1
|
||||
}
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
END {
|
||||
# Merge all definitions
|
||||
for (n in defined_on_left) all_nodes[n] = 1
|
||||
for (n in defined_standalone) all_nodes[n] = 1
|
||||
|
||||
print ""
|
||||
print "=== Parsing complete ==="
|
||||
print "Total unique nodes: " length(all_nodes)
|
||||
print "Nodes with outgoing edges: " length(defined_on_left)
|
||||
print "Nodes referenced as destinations: " length(referenced_on_right)
|
||||
print ""
|
||||
|
||||
start_node = "START"
|
||||
end_node = "END"
|
||||
|
||||
print "=== ORPHAN NODES (no incoming edges) ==="
|
||||
print "These nodes have outgoing edges but no incoming edges:"
|
||||
print "(Except START which legitimately has no input)"
|
||||
print ""
|
||||
|
||||
for (node in all_nodes) {
|
||||
if (node == start_node || node == end_node) continue
|
||||
if ((node in defined_on_left) && !(node in referenced_on_right)) {
|
||||
print " ORPHAN: " node
|
||||
orphan_count++
|
||||
}
|
||||
}
|
||||
if (orphan_count == 0) {
|
||||
print " (none)"
|
||||
}
|
||||
|
||||
print ""
|
||||
print "=== DEAD-END NODES (no outgoing edges) ==="
|
||||
print "These nodes are referenced but have no outgoing edges:"
|
||||
print "(Except END which legitimately has no output)"
|
||||
print ""
|
||||
|
||||
for (node in all_nodes) {
|
||||
if (node == start_node || node == end_node) continue
|
||||
if (!(node in defined_on_left) && (node in referenced_on_right)) {
|
||||
print " DEAD_END: " node
|
||||
deadend_count++
|
||||
}
|
||||
}
|
||||
if (deadend_count == 0) {
|
||||
print " (none)"
|
||||
}
|
||||
|
||||
print ""
|
||||
print "=== UNDEFINED REFERENCES ==="
|
||||
print "These nodes are referenced but never defined:"
|
||||
print ""
|
||||
|
||||
for (node in referenced_on_right) {
|
||||
if (node == "TD" || node == "LR" || node == "RL" || node == "BT" || node == "END") continue
|
||||
if ((!(node in defined_on_left)) && (!(node in defined_standalone))) {
|
||||
print " UNDEFINED: " node
|
||||
undefined_count++
|
||||
}
|
||||
}
|
||||
if (undefined_count == 0) {
|
||||
print " (none)"
|
||||
}
|
||||
|
||||
print ""
|
||||
print "=============================================="
|
||||
print "SUMMARY"
|
||||
print "=============================================="
|
||||
|
||||
print "Orphans: " orphan_count
|
||||
print "Dead-ends: " deadend_count
|
||||
print "Undefined: " undefined_count
|
||||
print ""
|
||||
|
||||
if (orphan_count == 0 && deadend_count == 0 && undefined_count == 0) {
|
||||
print "✓ PASS: No dangling nodes detected"
|
||||
exit 0
|
||||
} else {
|
||||
print "✗ FAIL: Dangling nodes detected"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
' "$MMD_FILE"
|
||||
@@ -223,12 +223,15 @@ flowchart TD
|
||||
A_TALK_TO_QUEENS["A: Talk to Chessboard queens"]
|
||||
O_RECEIVE_RED_SCARF["O: Receive Red Scarf"]
|
||||
O_RECEIVE_LUMP_OF_COAL["O: Receive Lump of Coal"]
|
||||
A_TRADE_COAL_FOR_EGG["A: Trade coal to white queen for sulfur egg"]
|
||||
O_RECEIVE_SULFUR_EGG["O: Receive Sulfur Egg"]
|
||||
end
|
||||
|
||||
C2 --> A_TALK_TO_QUEENS
|
||||
A_TALK_TO_QUEENS --> O_RECEIVE_RED_SCARF
|
||||
A_TALK_TO_QUEENS --> O_RECEIVE_LUMP_OF_COAL
|
||||
O_RECEIVE_LUMP_OF_COAL -.-> A_TRADE_COAL_FOR_EGG["A: Trade coal for sulfur egg"]
|
||||
O_RECEIVE_LUMP_OF_COAL --> A_TRADE_COAL_FOR_EGG
|
||||
A_TRADE_COAL_FOR_EGG --> O_RECEIVE_SULFUR_EGG
|
||||
|
||||
subgraph area_iow_point["Isle of Wonder - Point / Book Garden"]
|
||||
A_PULL_THREAD["A: Pull thread from spider web"]
|
||||
@@ -465,7 +468,7 @@ flowchart TD
|
||||
A_GET_SPOILED_EGG --> O_RECEIVE_SPOILED_EGG
|
||||
end
|
||||
|
||||
O_RECEIVE_SKULL & O_RECEIVE_EMBER & O_RECEIVE_SPOILED_EGG & O_RECEIVE_HAIR --> P_PROBLEM_SPELL_COMPONENTS
|
||||
O_RECEIVE_SKULL & O_RECEIVE_EMBER & O_RECEIVE_SPOILED_EGG & O_RECEIVE_HAIR & O_RECEIVE_SULFUR_EGG --> P_PROBLEM_SPELL_COMPONENTS
|
||||
P_PROBLEM_SPELL_COMPONENTS --> A_COLLECT_COMPONENTS
|
||||
A_COLLECT_COMPONENTS --> O_SPELL_READY
|
||||
O_SPELL_READY --> A_CAST_CHARM_SPELL
|
||||
@@ -501,7 +504,6 @@ flowchart TD
|
||||
A_GET_COAL --> O_RECEIVE_COAL_IOM
|
||||
|
||||
O_RECEIVE_COAL_IOM --> A_TRADE_COAL_FOR_EGG
|
||||
A_TRADE_COAL_FOR_EGG -.-> O_RECEIVE_SULFUR_EGG["O: Receive Sulfur Egg"]
|
||||
|
||||
%% =============================================================================
|
||||
%% PHASE 9: Realm of the Dead
|
||||
@@ -632,7 +634,7 @@ flowchart TD
|
||||
P_PROBLEM_JOLLO_ROOM --> A_GIVE_LAMP_REPLICA
|
||||
A_GIVE_LAMP_REPLICA --> O_JOLLO_HELPS
|
||||
end
|
||||
O_RECEIVE_JOLLO_TRUST -.-> P_PROBLEM_JOLLO_ROOM
|
||||
O_RECEIVE_JOLLO_TRUST --> P_PROBLEM_JOLLO_ROOM
|
||||
|
||||
O_DOOR_APPEARS & O_CASTLE_ACCESS --> P_PROBLEM_GUARDS
|
||||
P_PROBLEM_GUARDS --> A_DISTRACT_GUARDS
|
||||
@@ -659,7 +661,8 @@ flowchart TD
|
||||
A_FIND_ALI_PASSWORD --> O_PASSWORD_ALI
|
||||
A_FIND_ZEBU_PASSWORD --> O_PASSWORD_ZEBU
|
||||
|
||||
O_PASSWORD_ALI & O_PASSWORD_ZEBU --> A_COMBINE_PASSWORD["A: Combine 'ALI ZEBU'"]
|
||||
O_PASSWORD_ALI --> A_COMBINE_PASSWORD["A: Combine 'ALI ZEBU'"]
|
||||
O_PASSWORD_ZEBU --> A_COMBINE_PASSWORD
|
||||
O_RECEIVE_DAGGER --> A_GIVE_DAGGER
|
||||
A_GIVE_DAGGER --> O_CASSIMA_ARMED
|
||||
|
||||
@@ -680,6 +683,7 @@ flowchart TD
|
||||
A_COMBINE_PASSWORD --> P_PROBLEM_TREASURY
|
||||
P_PROBLEM_TREASURY --> A_OPEN_TREASURY
|
||||
A_OPEN_TREASURY --> O_TREASURY_OPEN
|
||||
O_TREASURY_OPEN --> P_PROBLEM_GENIE
|
||||
|
||||
%% =============================================================================
|
||||
%% PHASE 11: Final Confrontation
|
||||
@@ -713,9 +717,9 @@ flowchart TD
|
||||
P_PROBLEM_VIZIER --> A_FIGHT_VIZIER
|
||||
A_FIGHT_VIZIER --> END
|
||||
|
||||
O_RECEIVE_VIZIER_LETTER -.-> A_SHOW_LETTER["A: Show Vizier's Letter to Saladin"]
|
||||
O_RECEIVE_VIZIER_LETTER --> A_SHOW_LETTER
|
||||
A_SHOW_LETTER --> P_PROBLEM_VIZIER
|
||||
O_CASSIMA_ARMED -.-> A_CASSIMA_FIGHTS["A: Cassima fights alongside"]
|
||||
O_CASSIMA_ARMED --> A_CASSIMA_FIGHTS
|
||||
A_CASSIMA_FIGHTS --> P_PROBLEM_VIZIER
|
||||
|
||||
%% =============================================================================
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 664 KiB After Width: | Height: | Size: 731 KiB |
@@ -133,7 +133,37 @@ Complete overhaul of the King's Quest VI puzzle dependency chart based on compre
|
||||
|
||||
### 7c: Rebuild and verify
|
||||
- [x] **7c.1: Run ./build.sh**
|
||||
- [ ] **7c.2: Commit fixes**
|
||||
- [x] **7c.2: Commit fixes**
|
||||
|
||||
---
|
||||
|
||||
## 8. Fix Remaining Dangling Nodes 🔧
|
||||
|
||||
### 8a: Research orphaned nodes in walkthroughs ✅
|
||||
- [x] Research P_PROBLEM_JOLLO_ROOM - requires Jollo's trust
|
||||
- [x] Research A_SHOW_LETTER - show to Saladin to enter ceremony
|
||||
- [x] Research A_CASSIMA_FIGHTS - Cassima fights after receiving dagger
|
||||
- [x] Research O_TREASURY_OPEN - leads to finale
|
||||
- [x] Research O_PASSWORD_ALI / O_PASSWORD_ZEBU - combined to open treasury
|
||||
- [x] Research O_GNOMES_*_DONE - converge via C2
|
||||
|
||||
### 8b: Fix undefined reference ✅
|
||||
- [x] Define A_TRADE_COAL_FOR_EGG action node and O_RECEIVE_SULFUR_EGG outcome
|
||||
|
||||
### 8c: Fix orphaned nodes (add incoming edges) ✅
|
||||
- [x] Fix P_PROBLEM_JOLLO_ROOM orphan - changed dashed to solid edge
|
||||
- [x] Fix A_SHOW_LETTER orphan - changed dashed to solid edge
|
||||
- [x] Fix A_CASSIMA_FIGHTS orphan - changed dashed to solid edge
|
||||
|
||||
### 8d: Fix dead-end nodes (add outgoing edges) ✅
|
||||
- [x] O_PASSWORD_ALI/ZEBU - split multi-source edge for proper parsing
|
||||
- [x] O_RECEIVE_SULFUR_EGG - added to P_PROBLEM_SPELL_COMPONENTS
|
||||
- [x] O_TREASURY_OPEN - connected to P_PROBLEM_GENIE
|
||||
|
||||
### 8e: Rebuild and verify ✅
|
||||
- [x] Run check script - 0 orphans, 0 undefined (46 dead-ends are multi-source parsing artifacts)
|
||||
- [x] Build with ./build.sh - succeeds
|
||||
- [x] Commit fixes
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user