Back to Projects

Sharpee Platform Status

A Modern TypeScript Interactive Fiction Platform

April 2025 - February 2026 | 10+ Months of Development
19 npm Packages 48 Stdlib Actions 127+ ADRs Written Zifmia Desktop App

Platform Overview

Sharpee is a modern, TypeScript-based Interactive Fiction platform designed for extensibility, clean separation of concerns, and developer experience. This report covers one month of intensive development since January 9, 2026 — the most transformative period in the project's history.

19
npm Packages
+8 since Jan 9
48
Stdlib Actions
+1 since Jan 9
200+
Grammar Patterns
127+
ADRs Written
+34 since Jan 9
3,500+
Unit Tests
+700 since Jan 9
0.9.86
Beta Version

Parser & Scope Overhaul (ADR-093)

Scope Enforcement Moved from Grammar to Actions

Complete architectural separation: grammar declares semantics (what kind of thing), actions enforce scope (can the player reach it). Six-phase refactor completed in one day.

5-Level Numeric Scope System

All 24 stdlib actions declare defaultScope + use context.requireScope()

// Scope levels (highest = most restrictive)
enum ScopeLevel {
  UNAWARE  = 0,  // Entity not known to player
  AWARE    = 1,  // Player knows it exists (can hear/smell)
  VISIBLE  = 2,  // Player can see it
  REACHABLE = 3, // Player can touch it
  CARRIED  = 4,  // In player's inventory
}

// Grammar: declares ONLY semantic constraints
grammar.forAction('if.action.opening')
  .verbs(['open'])
  .pattern(':target')
  .hasTrait('target', TraitType.OPENABLE)  // What kind of thing
  .build();

// Action: enforces scope dynamically
validate(context) {
  const check = context.requireScope(target, ScopeLevel.REACHABLE);
  if (!check.ok) return check.error!;
}

Implicit Take System

6 actions auto-take items when reachable but not carried

// Player types: "wear hat" (hat is on the floor, not in inventory)
// Engine: "(first taking the hat)" -> then wears it

const result = context.requireCarriedOrImplicitTake(item);
if (!result.ok) return result.error!;
// Applied to: dropping, inserting, giving, showing, throwing, wearing

Adjective Disambiguation (ADR-093)

Entities now declare adjectives on IdentityTrait. Parser scores adjective matches (+4 points) for multi-word disambiguation. "press yellow button" correctly resolves among 4 identically-named buttons.

Text Output Pipeline (ADR-095/096)

Stateless Text Service + Message Formatters

Complete rewrite of text output. FyreVM-inspired channel I/O. Pure function: events in, TextBlocks out.

Formatter System & Perspective Placeholders

42 action templates converted to perspective-aware output

// Article selection by noun type
{a:item}  // common -> "a sword" / "an apple"
          // proper -> "Excalibur" (no article)
          // mass -> "some water"

// List formatting with Oxford comma
{a:items:list}      // "a sword, a key, and a lantern"
{the:items:or-list} // "the north, the south, or the west"

// Perspective placeholders (42 template files updated)
'taken': "{You} {take} {the:item}."
// 2nd person: "You take the sword."
// 1st person: "I take the sword."
// 3rd person: "She takes the sword."

ADR-107: Dual-Mode Authored Content

Entities use literal text or message IDs for localization

// Either pattern works through the same API
description: "You are in a dark room"          // Literal text
description: "dungeo.msg.room.west_house"      // Message ID (localized)

// Single unified call handles both
const text = textService.getEntityText(entity, 'description', {}, world);

Engine Plugin Architecture (ADR-120)

Hardcoded Subsystems Become Plugins

Engine defines hook points in the turn cycle. NPCs, scheduler, and state machines are now plugins implementing TurnPlugin. Engine shrinks, becomes pure orchestrator.

TurnPlugin Contract

4 new packages extracted from engine core

interface TurnPlugin {
  id: string;
  priority: number;  // NPC=100, StateMachine=75, Scheduler=50
  onAfterAction(context: TurnPluginContext): ISemanticEvent[];
  getState?(): unknown;   // For save/restore
  setState?(state: unknown): void;
}

// Future subsystems don't modify engine - just create new plugins
engine.registerPlugin(new MyCustomPlugin());
Plugin Package Priority Responsibility
@sharpee/plugin-npc 100 NPC service (extracted from engine, -57 lines)
@sharpee/plugin-state-machine 75 Declarative FSM runtime (ADR-119)
@sharpee/plugin-scheduler 50 Daemon/fuse scheduler service
@sharpee/plugins Core TurnPlugin contract + PluginRegistry

State Machine Migrations (ADR-119)

Five imperative event handlers migrated to declarative state machines:

Machine States Pattern
Trapdoor open / closed Linear progression
Death Penalty alive / one_death / game_over Linear with CustomEffect scoring
Rainbow inactive / active Bidirectional toggle
Reality Altered inactive / shown Terminal
Victory playing / victory Terminal (replaced daemon polling)

Zifmia Story Runner (ADR-121/122)

Desktop Application + Story Bundle Format

Tauri v2 desktop app (~10MB) with React UI, .sharpee bundle format, rich media support, and multi-slot save/restore.

.sharpee Bundle Format

Zip archive: story.js (ESM) + meta.json + optional theme.css and assets/. Bundle size: ~147KB for Dungeo.

Tauri Desktop Client

8 Rust IPC commands. Saves to AppData. Windows MSI + macOS DMG + Linux DEB installers. Professional gold "Z" branding.

Save/Restore System

Multi-slot localStorage with lz-string compression (~70% reduction). Auto-save every turn. Delta-based world state serialization.

Rich Media (ADR-122/124)

Entity annotations with illustrations. Scoped CSS themes. Asset map plumbing via React context. Float layout for images.

Browser Client

DOS-era Infocom aesthetic. Command history, status line, PC speaker beep. eventemitter3 for browser compatibility.

Player Preferences

Illustration toggle and size settings. localStorage persistence. React context + usePreferences() hook.

Combat System

Canonical Melee Engine from 1981 MDL Source

Full combat system: FIGHT-STRENGTH formula, BLOW resolution, 9 outcome types, 3 result tables, wound healing, disengagement rules.

Melee Architecture

Story-level engine with platform interceptor hooks

// PC attacks NPC:
attacking.ts -> ActionInterceptor -> MeleeInterceptor (Dungeo)
                                  -> BasicCombatInterceptor (generic)

// NPC attacks PC:
npc-service.ts -> NpcCombatResolver -> meleeNpcResolver (Dungeo)
                                    -> basicNpcResolver (generic)

// No combat extension loaded:
"Violence is not the answer." // validation block
Component Details
melee.ts 390 lines. Canonical FIGHT-STRENGTH, BLOW resolution, 9 outcomes
melee-tables.ts 189 lines. DEF1/DEF2/DEF3 result tables from MDL source
melee-messages.ts 273 lines. 5 weapon tables, 3-7 variants per outcome
@sharpee/ext-basic-combat New package: generic combat for stories without custom melee
DIAGNOSE command Wound status messages matching MDL melee.137:302-324
Cure daemon CURE-CLOCK: heals 1 wound per 30 turns

Event Chaining & Domain Events (ADR-094/106)

Event Chain Registration

Cascade, override, and keyed registration modes

// Chain: opening a container automatically reveals contents
world.chainEvent('if.event.opened', (event, world) => {
  const items = getVisibleContents(event.data.entityId);
  if (items.length === 0) return null;
  return { type: 'if.event.revealed', data: { items } };
}, { mode: 'keyed', key: 'stdlib.chain.opened-revealed' });

// Transaction grouping
//   if.event.opened   { transactionId: 'txn-123', chainDepth: 0 }
//   if.event.revealed  { transactionId: 'txn-123', chainDepth: 1 }

Domain Events Migration (ADR-106)

25+ actions migrated from action.success to domain events

// Before: Generic action.success with messageId
world.createEvent('action.success', { messageId: 'if.action.taking.taken' });

// After: Domain event with typed data
world.createEvent('if.event.took', {
  messageId: 'if.action.taking.taken',
  params: { item: itemName, container: containerName },
});

Action Interceptors (ADR-118/126)

Story-Level Action Customization Without Engine Changes

Pre/post hooks on stdlib actions for story-specific puzzle logic. Destination interceptors for room entry conditions.

Hook Point Purpose
preValidate Block action before validation (e.g., hot axe prevents taking)
postValidate Add requirements after standard validation
preExecute Modify state before execution
postExecute React to execution (e.g., trap triggers)
postReport Add custom messages to output

9 stdlib actions with interceptor support: going, putting, throwing, pushing, entering, taking, attacking, switching_on, dropping.

ADR-126: Destination Interceptors

Going action checks BOTH source room interceptors (for if.action.going) and destination room interceptors (for if.action.entering_room). Used for gas room explosion on entry with lit flame source.

New Packages (8 Added)

New
@sharpee/plugins
New
@sharpee/plugin-npc
New
@sharpee/plugin-scheduler
New
@sharpee/plugin-state-machine
New
@sharpee/text-blocks
New
@sharpee/text-service
New
@sharpee/ext-basic-combat
New
@sharpee/zifmia
@sharpee/core
@sharpee/world-model
@sharpee/engine
@sharpee/stdlib
@sharpee/parser-en-us
@sharpee/lang-en-us
@sharpee/if-domain
@sharpee/platforms/*
@sharpee/transcript-tester
@sharpee/sharpee

Dungeo (Zork) Story Progress

12 Walkthrough Chains | 321+ Tests Passing

Canonical fidelity verified against 1981 MDL source (mdlzork_810722). Score: 281/616 implemented. Troll, Thief, and Cyclops NPCs complete with full combat.

Troll Implementation (9/9 MDL features)

Three combat states, NPC inventory, 3-hit kill, 2-turn recovery, axe white-hot blocking, death with sinister smoke.

Tea Room Puzzle Area

Eat-me cake shrink/teleport, red cake dissolves pool, cage/sphere trap with 10-turn poison gas, Low Room carousel.

Combat NPCs

Troll: canonical combat, axe mechanics. Thief: WINNING? AI, egg opening vulnerability. Combat disengagement + wound healing.

Maze Topology Audit

Complete rewrite vs MDL source. 15 twisty rooms + 7 coal mine rooms corrected. Self-loops restored.

Scoring System

118 pts treasure taking, 98 pts trophy case, 45 pts room visits, 20 pts achievements. OTVAL/OFVAL fixed across 14 files.

Playtest Transcripts

TR-001 (5 issues), TR-002 (43 discrepancies), TR-003 (6 gaps) — all resolved against canonical 1994 DOS version.

Transcript Tester Improvements

Feature Syntax Purpose
Loop construct [DO] ... [UNTIL "text"] Variable-length combat sequences
Retry blocks [RETRY: max=5] ... [ENSURES: ...] [END RETRY] Handle combat randomness with state save/restore
Multi-match [OK: contains_any "a" "b" "c"] Validate against multiple acceptable outputs
OR patterns [UNTIL "win" OR "die"] Loop exit on any matching condition

Architecture Decision Records (34 New)

ADR-093 I18N-Aware Entity Vocabulary

Adjective disambiguation on IdentityTrait

ADR-094 Event Chaining

Cascade/override/keyed chain modes

ADR-095 Message Template Formatters

Article, list, text formatters with chaining

ADR-096 Text Service Architecture

Stateless FyreVM-inspired channel I/O

ADR-102/103 Dialogue & CYOA Architecture

Pluggable dialogue extensions, narrative-as-code

ADR-104 Implicit Inference & Take

Pronoun-triggered inference, auto-take for verbs

ADR-106/107 Domain Events & Dual-Mode Content

Event sourcing, literal + localized text

ADR-118 Action Interceptors

Pre/post hooks for story customization

ADR-119 Declarative State Machines

States, transitions, triggers, guards, effects

ADR-120 Engine Plugin Architecture

TurnPlugin contract, 4 extracted plugins

ADR-121/122 Story Runner & Rich Media

.sharpee bundles, Tauri, illustrations, CSS themes

ADR-124 Entity Annotations

Multiple annotations per entity, trigger conditions

ADR-125 Zifmia Panel System

CSS Grid windowing (transcript, grid, image, status)

ADR-126/127 Destination & Location Interceptors

Room entry conditions, location-scoped actions

Plus ADR-097 (React Client), ADR-099/100 (GLK/Accessibility), ADR-123 (Daemon Hierarchy, partially accepted), and approximately 16 additional ADRs in the 108-117 range covering engine remediation, service extraction, and build system changes.

Infrastructure & Build System

Build System Overhaul

Dual TSConfig (CJS + ESM). 19 tsconfig.esm.json files. Explicit --alias flags for bundle resolution. 10 scripts consolidated to 3.

Engine Service Extraction

4 services extracted: VocabularyManager, SaveRestoreService, TurnEventProcessor, PlatformOperationHandler. Engine reduced 21%.

Website Rebuild

Plain Astro + Expressive Code. 18 pages. API reference for traits, actions, grammar. DSLM pattern catalog with D3.js visualization (124 patterns).

Documentation Overhaul

792 old session files archived. 5 obsolete doc folders deleted. README updated for 0.9.85. Comprehensive API reference (traits, actions, grammar).

Development Timeline

January 10, 2026

Parser Refactor Complete (6 Phases)

Scope enforcement moved from grammar to actions. ScopeLevel enum, defaultScope on 24 actions, implicit takes for 6 actions. PR #49 merged.

January 12, 2026

ADR-093: Adjective Disambiguation + ADR-094: Event Chaining

IdentityTrait adjectives. Event chains with cascade/override/keyed modes. opened->revealed chain. 38 new tests.

January 13, 2026

ADR-095/096: Text Pipeline + Perspective Placeholders

Stateless TextService, formatter system, 42 template files updated. New text-blocks and text-service packages.

January 14-16, 2026

ADR-104: Implicit Inference + Engine Remediation

Implicit object inference, implicit take. 4 services extracted from GameEngine (-21%). Event-adapter reduced 44%.

January 17-18, 2026

Universal Capability Dispatch + Browser Client

5 core dispatch functions, resolution modes. Browser save/restore with multi-slot localStorage. DOS-era Infocom aesthetic.

January 19, 2026

ADR-106/107: Domain Events Migration

25+ actions migrated to domain events. Dual-mode authored content. Build scripts consolidated.

January 20-27, 2026

ADR-118: Action Interceptors + Handler Audit

Interceptor pattern for 9 actions. Full handler mutation audit (25 files). Dam state consolidation. FORTRAN source fidelity.

January 28, 2026

ADR-120: Engine Plugin Architecture (4 Phases)

4 new packages: plugins, plugin-npc, plugin-scheduler, plugin-state-machine. Engine becomes pure orchestrator.

January 29-31, 2026

Zifmia Runner (7 Phases) + Tauri Desktop

Bundle loader, storage, illustrations, CSS scoping, preferences, 8 Rust IPC commands. Inline SAVE/RESTORE dialogs.

February 1, 2026

Website Rebuild + Build System Overhaul

Astro site with 18 pages. Dual TSConfig architecture. Critical bundle resolution fix (--alias flags). StoryInfoTrait.

February 6-7, 2026

Tea Room Puzzle Area (5 Phases)

Eat-me cakes, cage/sphere trap, Low Room carousel, robot commands. 9 room corrections. 35+ transcript tests.

February 7-11, 2026

Combat System (Canonical Melee Engine)

390-line melee engine from MDL source. @sharpee/ext-basic-combat package. Troll/Thief/Cyclops integration. DO/UNTIL transcript loops.

Platform Quality Assessment

Overall Grade: A+

The platform has undergone its most transformative month: 8 new packages, a plugin architecture, a desktop application, a canonical combat system, and comprehensive text pipeline. 379 development sessions produced 34 new ADRs and 700+ new tests.

Aspect Rating Key Improvement (Since Jan 9)
Architecture Excellent Plugin architecture, scope separation, interceptors
Extensibility Excellent TurnPlugin, ActionInterceptors, CapabilityBehaviors
Text Pipeline Excellent Stateless service, formatters, perspective support
Parser Excellent Trait-based grammar, adjective disambiguation, implicit takes
Desktop Client New Zifmia: Tauri app, .sharpee bundles, multi-slot saves
Combat System New Canonical melee engine, generic combat extension
State Machines New Declarative FSM plugin, 5 handlers migrated
Test Coverage Excellent 3,500+ tests, 12 walkthroughs, DO/UNTIL loops
Documentation Excellent 127+ ADRs, API reference, pattern catalog (124 patterns)
Story Fidelity Excellent MDL source verification, 43 discrepancies resolved

What's Next

Category Remaining Work
Dungeo Completion Remaining treasures (335/616 pts), endgame sequence, Thief roaming AI
ADR-089 Phase E Advanced verb conjugation, past tense support
ADR-125 Implementation Zifmia panel/windowing system (CSS Grid layout)
ADR-127 Implementation Location-scoped interceptors (room-level action hooks)
Grammar Migration Remaining verbs to .forAction() API
GenAI Layer Story spec templates, LLM code generation pipeline
NPC System Conversation trees, behavior scheduling, dialogue extensions
Accessibility Screen reader support (ADR-100), GLK compatibility (ADR-099)