Created
March 24, 2025 18:08
-
-
Save bfollington/93d3d4b312a959a5fe16b8ba45c50665 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Multi-Timescale Explorer | |
* | |
* A framework for creative technological exploration that balances | |
* present engagement with future direction. | |
*/ | |
namespace MultiTimescaleExplorer { | |
// ======== Core Types ======== | |
/** | |
* Represents an open-ended inquiry or area of exploration | |
*/ | |
interface Inquiry { | |
id: string; | |
question: string; | |
connections: string[]; // IDs of related inquiries | |
notes: string; | |
dateCreated: Date; | |
lastEngaged: Date; | |
} | |
/** | |
* A concrete experiment to explore an Inquiry | |
*/ | |
interface Experiment { | |
id: string; | |
title: string; | |
inquiryIds: string[]; // Which inquiries does this experiment address? | |
hypothesis: string; | |
approach: string; | |
status: "planned" | "in-progress" | "completed" | "abandoned"; | |
results?: string; | |
reflections?: string; | |
dateCreated: Date; | |
lastUpdated: Date; | |
timeInvested: number; // In minutes | |
} | |
/** | |
* A tangible outcome that can be shared with others | |
*/ | |
interface Artifact { | |
id: string; | |
title: string; | |
description: string; | |
experimentIds: string[]; // Which experiments led to this? | |
type: "tool" | "prototype" | "writing" | "visualization" | "other"; | |
feedback: Interaction[]; | |
dateCreated: Date; | |
lastUpdated: Date; | |
narrativeFraming?: string; // How this fits into your broader story | |
audienceContext?: string; // How to present this to specific audiences | |
} | |
/** | |
* Record of someone interacting with your work | |
*/ | |
interface Interaction { | |
id: string; | |
artifactId: string; | |
person: string; | |
notes: string; | |
insights: string; | |
followupInquiryIds?: string[]; // New inquiries sparked | |
date: Date; | |
} | |
// Additional types for external-facing elements | |
/** | |
* A cohesive theme that ties together multiple artifacts | |
*/ | |
interface Narrative { | |
id: string; | |
name: string; | |
description: string; | |
keyFraming: string; // The core story you're telling | |
associatedArtifactIds: string[]; | |
targetAudiences: Audience[]; | |
dateCreated: Date; | |
lastUpdated: Date; | |
} | |
/** | |
* Represents a specific audience you might present your work to | |
*/ | |
interface Audience { | |
id: string; | |
name: string; // e.g., "Tech startups", "Game developers", "Academic researchers" | |
interests: string[]; | |
painPoints: string[]; | |
keyValues: string[]; | |
communicationChannels: string[]; | |
relevantExperiences: string[]; | |
} | |
// ======== Core System ======== | |
class Explorer { | |
private inquiries: Map<string, Inquiry> = new Map(); | |
private experiments: Map<string, Experiment> = new Map(); | |
private artifacts: Map<string, Artifact> = new Map(); | |
private interactions: Map<string, Interaction> = new Map(); | |
private narratives: Map<string, Narrative> = new Map(); | |
private audiences: Map<string, Audience> = new Map(); | |
// Timescale management | |
private currentFocus?: string; // ID of current inquiry/experiment | |
private todaysMicroGoal?: string; | |
private weeklyThemes: string[] = []; | |
private quarterlyDirections: string[] = []; | |
/** | |
* Records time spent in active exploration mode | |
*/ | |
trackExplorationSession(experimentId: string, minutes: number, notes: string): void { | |
const experiment = this.experiments.get(experimentId); | |
if (!experiment) throw new Error("Experiment not found"); | |
experiment.timeInvested += minutes; | |
experiment.lastUpdated = new Date(); | |
if (notes) { | |
experiment.results = experiment.results | |
? `${experiment.results}\n\n${new Date().toISOString()}: ${notes}` | |
: `${new Date().toISOString()}: ${notes}`; | |
} | |
// Update related inquiries' lastEngaged date | |
experiment.inquiryIds.forEach(id => { | |
const inquiry = this.inquiries.get(id); | |
if (inquiry) inquiry.lastEngaged = new Date(); | |
}); | |
} | |
/** | |
* Core method for embracing serendipity - creates a new inquiry | |
* from an unexpected insight | |
*/ | |
captureNewInquiry(question: string, notes: string = "", connections: string[] = []): string { | |
const id = generateId(); | |
const now = new Date(); | |
this.inquiries.set(id, { | |
id, | |
question, | |
connections, | |
notes, | |
dateCreated: now, | |
lastEngaged: now | |
}); | |
return id; | |
} | |
/** | |
* Creates an experiment to explore one or more inquiries | |
*/ | |
designExperiment(title: string, inquiryIds: string[], hypothesis: string, approach: string): string { | |
const id = generateId(); | |
const now = new Date(); | |
// Validate that inquiries exist | |
inquiryIds.forEach(id => { | |
if (!this.inquiries.has(id)) throw new Error(`Inquiry ${id} not found`); | |
}); | |
this.experiments.set(id, { | |
id, | |
title, | |
inquiryIds, | |
hypothesis, | |
approach, | |
status: "planned", | |
dateCreated: now, | |
lastUpdated: now, | |
timeInvested: 0 | |
}); | |
return id; | |
} | |
/** | |
* Creates a shareable artifact from experiment results | |
*/ | |
createArtifact(title: string, description: string, experimentIds: string[], type: Artifact["type"]): string { | |
const id = generateId(); | |
const now = new Date(); | |
// Validate that experiments exist | |
experimentIds.forEach(id => { | |
if (!this.experiments.has(id)) throw new Error(`Experiment ${id} not found`); | |
}); | |
this.artifacts.set(id, { | |
id, | |
title, | |
description, | |
experimentIds, | |
type, | |
feedback: [], | |
dateCreated: now, | |
lastUpdated: now | |
}); | |
return id; | |
} | |
/** | |
* Records interaction and feedback from someone engaging with your work | |
*/ | |
recordInteraction(artifactId: string, person: string, notes: string, insights: string): string { | |
const id = generateId(); | |
if (!this.artifacts.has(artifactId)) throw new Error(`Artifact ${artifactId} not found`); | |
const interaction: Interaction = { | |
id, | |
artifactId, | |
person, | |
notes, | |
insights, | |
date: new Date() | |
}; | |
this.interactions.set(id, interaction); | |
// Add to artifact's feedback | |
const artifact = this.artifacts.get(artifactId)!; | |
artifact.feedback.push(interaction); | |
return id; | |
} | |
// ======== Narrative Management Methods ======== | |
/** | |
* Creates a narrative that ties together multiple artifacts | |
* into a cohesive story for external audiences | |
*/ | |
createNarrative(name: string, description: string, keyFraming: string): string { | |
const id = generateId(); | |
const now = new Date(); | |
this.narratives.set(id, { | |
id, | |
name, | |
description, | |
keyFraming, | |
associatedArtifactIds: [], | |
targetAudiences: [], | |
dateCreated: now, | |
lastUpdated: now | |
}); | |
return id; | |
} | |
/** | |
* Adds an artifact to a narrative | |
*/ | |
addArtifactToNarrative(narrativeId: string, artifactId: string, framingContext?: string): void { | |
const narrative = this.narratives.get(narrativeId); | |
const artifact = this.artifacts.get(artifactId); | |
if (!narrative) throw new Error(`Narrative ${narrativeId} not found`); | |
if (!artifact) throw new Error(`Artifact ${artifactId} not found`); | |
narrative.associatedArtifactIds.push(artifactId); | |
narrative.lastUpdated = new Date(); | |
// Add narrative framing to the artifact if provided | |
if (framingContext) { | |
artifact.narrativeFraming = framingContext; | |
} | |
} | |
/** | |
* Defines a specific audience you might present work to | |
*/ | |
defineAudience(name: string, interests: string[], painPoints: string[], | |
keyValues: string[], communicationChannels: string[]): string { | |
const id = generateId(); | |
this.audiences.set(id, { | |
id, | |
name, | |
interests, | |
painPoints, | |
keyValues, | |
communicationChannels, | |
relevantExperiences: [] | |
}); | |
return id; | |
} | |
/** | |
* Associates an audience with a narrative | |
*/ | |
addAudienceToNarrative(narrativeId: string, audienceId: string): void { | |
const narrative = this.narratives.get(narrativeId); | |
const audience = this.audiences.get(audienceId); | |
if (!narrative) throw new Error(`Narrative ${narrativeId} not found`); | |
if (!audience) throw new Error(`Audience ${audienceId} not found`); | |
narrative.targetAudiences.push(audience); | |
narrative.lastUpdated = new Date(); | |
} | |
/** | |
* Customizes how an artifact should be presented to a specific audience | |
*/ | |
customizeArtifactForAudience(artifactId: string, audienceId: string, presentationContext: string): void { | |
const artifact = this.artifacts.get(artifactId); | |
const audience = this.audiences.get(audienceId); | |
if (!artifact) throw new Error(`Artifact ${artifactId} not found`); | |
if (!audience) throw new Error(`Audience ${audienceId} not found`); | |
artifact.audienceContext = presentationContext; | |
artifact.lastUpdated = new Date(); | |
} | |
/** | |
* Generates presentation materials for a narrative targeted at a specific audience | |
*/ | |
createPresentationForAudience(narrativeId: string, audienceId: string): string { | |
const narrative = this.narratives.get(narrativeId); | |
const audience = this.audiences.get(audienceId); | |
if (!narrative) throw new Error(`Narrative ${narrativeId} not found`); | |
if (!audience) throw new Error(`Audience ${audienceId} not found`); | |
// This would be a more complex method in practice | |
// For now, a simple placeholder that could generate slide decks, pitches, etc. | |
return `Presentation for ${audience.name} about "${narrative.name}"`; | |
} | |
/** | |
* Analyzes themes across artifacts to suggest potential narrative framings | |
*/ | |
suggestNarratives(): string[] { | |
// This would use more sophisticated analysis in practice | |
// For now, a placeholder | |
return [ | |
"Tools for sovereign knowledge management", | |
"Rethinking human-computer interfaces for creativity", | |
"Decentralized systems for personal agency" | |
]; | |
} | |
/** | |
* Creates a new inquiry based on feedback, connecting the cycle | |
*/ | |
createInquiryFromInteraction(interactionId: string, question: string, notes: string = ""): string { | |
const interaction = this.interactions.get(interactionId); | |
if (!interaction) throw new Error(`Interaction ${interactionId} not found`); | |
const inquiryId = this.captureNewInquiry(question, notes, []); | |
// Connect this new inquiry to the interaction | |
interaction.followupInquiryIds = interaction.followupInquiryIds || []; | |
interaction.followupInquiryIds.push(inquiryId); | |
return inquiryId; | |
} | |
// ======== Reflection Methods ======== | |
/** | |
* Identify inquiries that haven't been engaged with recently | |
*/ | |
findDormantInquiries(daysSinceLastEngagement: number = 30): Inquiry[] { | |
const cutoffDate = new Date(); | |
cutoffDate.setDate(cutoffDate.getDate() - daysSinceLastEngagement); | |
return Array.from(this.inquiries.values()) | |
.filter(inquiry => inquiry.lastEngaged < cutoffDate); | |
} | |
/** | |
* Find inquiries that connect to many others - potential rich areas | |
*/ | |
findHighlyConnectedInquiries(minConnections: number = 3): Inquiry[] { | |
return Array.from(this.inquiries.values()) | |
.filter(inquiry => inquiry.connections.length >= minConnections); | |
} | |
/** | |
* Calculate time investment across different areas | |
*/ | |
analyzeTimeAllocation(): Record<string, number> { | |
const inquiryTimeMap: Record<string, number> = {}; | |
// Initialize with 0 minutes for all inquiries | |
for (const inquiry of this.inquiries.values()) { | |
inquiryTimeMap[inquiry.id] = 0; | |
} | |
// Add up time from experiments | |
for (const experiment of this.experiments.values()) { | |
experiment.inquiryIds.forEach(id => { | |
// Distribute time equally among all inquiries of this experiment | |
const timePerInquiry = experiment.timeInvested / experiment.inquiryIds.length; | |
inquiryTimeMap[id] = (inquiryTimeMap[id] || 0) + timePerInquiry; | |
}); | |
} | |
return inquiryTimeMap; | |
} | |
/** | |
* Suggest new connections between inquiries based on common themes | |
*/ | |
suggestNewConnections(): {inquiry1: Inquiry, inquiry2: Inquiry, reason: string}[] { | |
const suggestions: {inquiry1: Inquiry, inquiry2: Inquiry, reason: string}[] = []; | |
const inquiries = Array.from(this.inquiries.values()); | |
// This would use NLP in a real implementation | |
// For now, just a placeholder that could be built out | |
return suggestions; | |
} | |
// ======== Planning Methods ======== | |
/** | |
* Set focus for current day based on energy and interest | |
*/ | |
setDailyFocus(inquiryOrExperimentId: string, microGoal: string): void { | |
// Validate ID belongs to either an inquiry or experiment | |
if (!this.inquiries.has(inquiryOrExperimentId) && !this.experiments.has(inquiryOrExperimentId)) { | |
throw new Error("Invalid inquiry or experiment ID"); | |
} | |
this.currentFocus = inquiryOrExperimentId; | |
this.todaysMicroGoal = microGoal; | |
} | |
/** | |
* Define themes for week - more directional than daily focus | |
*/ | |
setWeeklyThemes(themes: string[]): void { | |
this.weeklyThemes = themes; | |
} | |
/** | |
* Set broader directional areas for the quarter | |
*/ | |
setQuarterlyDirections(directions: string[]): void { | |
this.quarterlyDirections = directions; | |
} | |
/** | |
* Balances immediate focus with long-term direction | |
*/ | |
suggestNextAction(): string { | |
// This would be a sophisticated algorithm in practice | |
// For now, a simple placeholder | |
const actions = [ | |
"Spend 20 minutes exploring a dormant inquiry", | |
"Document insights from your most recent experiment", | |
"Share a recent artifact with someone new", | |
"Connect two previously unrelated inquiries", | |
"Design a micro-experiment you can complete today" | |
]; | |
return actions[Math.floor(Math.random() * actions.length)]; | |
} | |
} | |
// Helper function | |
function generateId(): string { | |
return Math.random().toString(36).substring(2, 15); | |
} | |
} | |
// ======== Example Usage ======== | |
// Create a new explorer | |
const explorer = new MultiTimescaleExplorer.Explorer(); | |
// Record some initial inquiries | |
const toolsForThoughtId = explorer.captureNewInquiry( | |
"How might we create tools that augment human cognition in non-prescriptive ways?", | |
"Inspired by Engelbart's work but moving beyond productivity toward creativity and agency" | |
); | |
const p2pKnowledgeId = explorer.captureNewInquiry( | |
"How can peer-to-peer systems enable more sovereign knowledge ecosystems?", | |
"Want to explore alternatives to centralized knowledge repositories" | |
); | |
const gameAsExplorationId = explorer.captureNewInquiry( | |
"Can games be designed as open-ended exploratory spaces rather than challenge-reward systems?", | |
"Looking at games as thinking tools rather than entertainment products" | |
); | |
// Connect related inquiries | |
// (In a real implementation, you'd have methods to manage these connections) | |
// Design an experiment | |
const experimentId = explorer.designExperiment( | |
"Prototype: Visual Programming Environment for Personal Knowledge", | |
[toolsForThoughtId, p2pKnowledgeId], | |
"A visual programming interface will make knowledge manipulation more intuitive for non-programmers", | |
"Build a simple prototype that allows spatial arrangement of knowledge fragments with visual rules for connection and transformation" | |
); | |
// Record time spent working on it | |
explorer.trackExplorationSession( | |
experimentId, | |
120, | |
"Initial prototype revealed that visual rule creation is harder than expected. Users gravitated toward spatial arrangement but struggled with formal rule definition." | |
); | |
// Create a shareable artifact | |
const artifactId = explorer.createArtifact( | |
"Spatial Knowledge Canvas - Alpha", | |
"A web-based tool for arranging and connecting ideas visually with simple automation rules", | |
[experimentId], | |
"tool" | |
); | |
// Record feedback from someone who tried it | |
const interactionId = explorer.recordInteraction( | |
artifactId, | |
"Alex", | |
"Loved the spatial arrangement, found the rule creation confusing but was excited about the possibilities", | |
"Suggested that instead of formal rules, the system could observe patterns in how they organize information and suggest automations" | |
); | |
// Define audiences | |
const developerAudienceId = explorer.defineAudience( | |
"Web Developers", | |
["Building better tools", "Reducing cognitive load", "Visual programming"], | |
["Complex codebases", "Documentation burden", "Keeping up with changing tech"], | |
["Elegance", "Efficiency", "Open source"], | |
["GitHub", "Twitter", "Dev conferences", "Tech blogs"] | |
); | |
const designerAudienceId = explorer.defineAudience( | |
"UX Designers", | |
["Information architecture", "Visual thinking", "Design systems"], | |
["Translating ideas to developers", "Complex requirements", "Rigid tools"], | |
["Creativity", "User empowerment", "Accessibility"], | |
["Dribbble", "Design conferences", "Medium", "Design podcasts"] | |
); | |
// Create a narrative | |
const knowledgeToolsNarrativeId = explorer.createNarrative( | |
"Visual Knowledge Systems", | |
"Spatial interfaces for personal knowledge management that adapt to user behavior", | |
"We're exploring how spatial interfaces combined with subtle machine learning can create knowledge tools that enhance human cognition without imposing rigid structures." | |
); | |
// Add artifacts to the narrative | |
explorer.addArtifactToNarrative( | |
knowledgeToolsNarrativeId, | |
artifactId, | |
"The Spatial Knowledge Canvas is our first experiment in creating tools that respect the natural ways humans organize information spatially while adding computational power." | |
); | |
// Associate audiences with narrative | |
explorer.addAudienceToNarrative(knowledgeToolsNarrativeId, developerAudienceId); | |
explorer.addAudienceToNarrative(knowledgeToolsNarrativeId, designerAudienceId); | |
// Customize presentation for different audiences | |
explorer.customizeArtifactForAudience( | |
artifactId, | |
developerAudienceId, | |
"For developers, we emphasize the reactive architecture and the pattern recognition system that suggests automations based on user behavior." | |
); | |
explorer.customizeArtifactForAudience( | |
artifactId, | |
designerAudienceId, | |
"For designers, we focus on how the spatial canvas enables more intuitive information architecture and creates a new design space for knowledge representation." | |
); | |
// This feedback sparks a new inquiry | |
const newInquiryId = explorer.createInquiryFromInteraction( | |
interactionId, | |
"How might we design systems that learn from user behavior without requiring explicit rule creation?", | |
"Pattern recognition vs. explicit programming - where is the right balance for non-technical users?" | |
); | |
// Set focuses at different timescales | |
explorer.setDailyFocus(newInquiryId, "Research pattern recognition approaches in no-code tools"); | |
explorer.setWeeklyThemes(["Learning system behavior", "Implicit vs explicit knowledge"]); | |
explorer.setQuarterlyDirections(["Tools for thought", "Agency-enhancing interfaces"]); | |
// Get a suggestion for what to do next | |
const nextAction = explorer.suggestNextAction(); | |
console.log(`Suggested next action: ${nextAction}`); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment