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

@@ -39,8 +39,6 @@ impl RelationshipKey {
#[derive(Debug, Clone)]
struct RelationshipDecl {
relationship: Relationship,
/// Which participant is "self" (index into participants)
self_index: Option<usize>,
}
/// Resolved bidirectional relationship
@@ -57,10 +55,8 @@ pub struct ResolvedRelationship {
pub struct ParticipantFields {
pub participant_name: Vec<String>,
pub role: Option<String>,
/// Fields from this participant's "self" block
pub self_fields: Vec<Field>,
/// Fields from this participant's "other" block (about other participants)
pub other_fields: Vec<Field>,
/// Fields from this participant's block
pub fields: Vec<Field>,
}
/// Resolve bidirectional relationships in a file
@@ -76,18 +72,11 @@ pub fn resolve_relationships(file: &File) -> Result<Vec<ResolvedRelationship>> {
let key = RelationshipKey::new(participant_names, rel.name.clone());
// Determine which participant is "self" based on self/other blocks
let self_index = rel
.participants
.iter()
.position(|p| p.self_block.is_some() || p.other_block.is_some());
relationship_groups
.entry(key)
.or_default()
.push(RelationshipDecl {
relationship: rel.clone(),
self_index,
});
}
}
@@ -122,43 +111,31 @@ fn merge_relationship_declarations(
.map(|p| ParticipantFields {
participant_name: p.name.clone(),
role: p.role.clone(),
self_fields: p.self_block.clone().unwrap_or_default(),
other_fields: p.other_block.clone().unwrap_or_default(),
fields: p.fields.clone(),
})
.collect();
// Merge shared fields (fields outside participant blocks)
let mut merged_fields = base.fields.clone();
// Merge additional declarations
for decl in decls.iter().skip(1) {
// If this declaration specifies a different participant as "self",
// merge their self/other blocks appropriately
if let Some(self_idx) = decl.self_index {
let participant_name = &decl.relationship.participants[self_idx].name;
// Merge participant fields
for participant in &decl.relationship.participants {
// Find this participant in our merged list
if let Some(idx) = participant_fields
if let Some(pf_idx) = participant_fields
.iter()
.position(|pf| &pf.participant_name == participant_name)
.position(|pf| pf.participant_name == participant.name)
{
// Merge self blocks
let self_block = decl.relationship.participants[self_idx]
.self_block
.clone()
.unwrap_or_default();
merge_fields(&mut participant_fields[idx].self_fields, self_block)?;
// Merge other blocks
let other_block = decl.relationship.participants[self_idx]
.other_block
.clone()
.unwrap_or_default();
merge_fields(&mut participant_fields[idx].other_fields, other_block)?;
// Merge fields for this participant
merge_fields(
&mut participant_fields[pf_idx].fields,
participant.fields.clone(),
)?;
}
}
}
// Merge shared fields (fields outside self/other blocks)
let mut merged_fields = base.fields.clone();
for decl in decls.iter().skip(1) {
// Merge shared relationship fields
merge_fields(&mut merged_fields, decl.relationship.fields.clone())?;
}
@@ -209,8 +186,7 @@ mod tests {
Participant {
name: vec![name.to_string()],
role: role.map(|s| s.to_string()),
self_block: None,
other_block: None,
fields: vec![],
span: Span::new(0, 10),
}
}
@@ -257,14 +233,12 @@ mod tests {
}
#[test]
fn test_bidirectional_relationship_merge() {
fn test_relationship_merge() {
let mut martha_participant = make_participant("Martha", Some("spouse"));
martha_participant.self_block = Some(vec![make_field("bond", 90)]);
martha_participant.other_block = Some(vec![make_field("trust", 85)]);
martha_participant.fields = vec![make_field("commitment", 90)];
let mut david_participant = make_participant("David", Some("spouse"));
david_participant.self_block = Some(vec![make_field("bond", 90)]);
david_participant.other_block = Some(vec![make_field("trust", 85)]);
david_participant.fields = vec![make_field("trust", 85)];
let file = File {
declarations: vec![
@@ -297,10 +271,10 @@ mod tests {
#[test]
fn test_conflicting_field_values() {
let mut p1 = make_participant("Alice", None);
p1.self_block = Some(vec![make_field("bond", 80)]);
p1.fields = vec![make_field("bond", 80)];
let mut p2 = make_participant("Alice", None);
p2.self_block = Some(vec![make_field("bond", 90)]); // Different value
p2.fields = vec![make_field("bond", 90)]; // Different value
let file = File {
declarations: vec![