release: Storybook v0.2.0 - Major syntax and features update

BREAKING CHANGES:
- Relationship syntax now requires blocks for all participants
- Removed self/other perspective blocks from relationships
- Replaced 'guard' keyword with 'if' for behavior tree decorators

Language Features:
- Add tree-sitter grammar with improved if/condition disambiguation
- Add comprehensive tutorial and reference documentation
- Add SBIR v0.2.0 binary format specification
- Add resource linking system for behaviors and schedules
- Add year-long schedule patterns (day, season, recurrence)
- Add behavior tree enhancements (named nodes, decorators)

Documentation:
- Complete tutorial series (9 chapters) with baker family examples
- Complete reference documentation for all language features
- SBIR v0.2.0 specification with binary format details
- Added locations and institutions documentation

Examples:
- Convert all examples to baker family scenario
- Add comprehensive working examples

Tooling:
- Zed extension with LSP integration
- Tree-sitter grammar for syntax highlighting
- Build scripts and development tools

Version Updates:
- Main package: 0.1.0 → 0.2.0
- Tree-sitter grammar: 0.1.0 → 0.2.0
- Zed extension: 0.1.0 → 0.2.0
- Storybook editor: 0.1.0 → 0.2.0
This commit is contained in:
2026-02-13 21:52:03 +00:00
parent 80332971b8
commit 16deb5d237
290 changed files with 90316 additions and 5827 deletions

View File

@@ -103,6 +103,84 @@ pub fn resolve_template_includes(
Ok(merged_fields)
}
// ===== Resource Linking Merge =====
/// Merge behavior links from templates into character
///
/// Algorithm (override-by-name semantics from design doc):
/// 1. Start with character's own behavior links (highest priority)
/// 2. For each template (in order), add its behavior links if not already
/// present by tree name
/// 3. Return concatenated list with character links first
///
/// This implements the merge semantics from
/// resource-linking-checkpoint2-addendum.md
pub fn merge_behavior_links(
character_links: Option<Vec<crate::syntax::ast::BehaviorLink>>,
template_links: Vec<Vec<crate::syntax::ast::BehaviorLink>>,
) -> Option<Vec<crate::syntax::ast::BehaviorLink>> {
use crate::syntax::ast::BehaviorLink;
// Start with character's own links
let mut result: Vec<BehaviorLink> = character_links.unwrap_or_default();
// Track which behavior trees are already linked (by tree path)
let mut seen_trees: HashSet<Vec<String>> =
result.iter().map(|link| link.tree.clone()).collect();
// Merge template links (in order)
for template_link_set in template_links {
for link in template_link_set {
// Only add if not already present (character overrides templates)
if !seen_trees.contains(&link.tree) {
seen_trees.insert(link.tree.clone());
result.push(link);
}
}
}
if result.is_empty() {
None
} else {
Some(result)
}
}
/// Merge schedule links from templates into character
///
/// Algorithm:
/// 1. Start with character's own schedule links
/// 2. For each template (in order), add its schedule links if not already
/// present
/// 3. Return concatenated list with character schedules first
pub fn merge_schedule_links(
character_schedules: Option<Vec<String>>,
template_schedules: Vec<Vec<String>>,
) -> Option<Vec<String>> {
// Start with character's own schedules
let mut result: Vec<String> = character_schedules.unwrap_or_default();
// Track which schedules are already linked
let mut seen_schedules: HashSet<String> = result.iter().cloned().collect();
// Merge template schedules (in order)
for template_schedule_set in template_schedules {
for schedule in template_schedule_set {
// Only add if not already present (character overrides templates)
if !seen_schedules.contains(&schedule) {
seen_schedules.insert(schedule.clone());
result.push(schedule);
}
}
}
if result.is_empty() {
None
} else {
Some(result)
}
}
/// Merge character templates into character fields
///
/// Algorithm:
@@ -499,6 +577,8 @@ mod tests {
fields,
includes: includes.iter().map(|s| s.to_string()).collect(),
strict,
uses_behaviors: None,
uses_schedule: None,
span: Span::new(0, 10),
}
}
@@ -513,6 +593,8 @@ mod tests {
} else {
Some(templates.iter().map(|s| s.to_string()).collect())
},
uses_behaviors: None,
uses_schedule: None,
span: Span::new(0, 10),
}
}