# Streamd Requirements Streamd (stylized as "Strea.md") is a personal knowledge management and time-tracking CLI tool that organizes time-ordered markdown files using `@Tag` annotations. ## Core Concepts ### Shard A **Shard** is the fundamental unit of content. It represents a section of a markdown file (paragraph, heading, list item) that can contain markers and tags. ``` Shard { markers: [String] // @Tag annotations at START of content tags: [String] // @Tag annotations AFTER content begins start_line: int end_line: int children: [Shard] // Nested shards (hierarchical) } ``` ### LocalizedShard A **LocalizedShard** extends Shard with temporal and dimensional placement information. ``` LocalizedShard { markers: [String] tags: [String] start_line: int end_line: int moment: DateTime // When this entry was created location: Map // Dimension placements children: [LocalizedShard] } ``` --- ## Tag Extraction Logic ### R1: Tag Recognition Pattern Tags are recognized by the regex pattern: `@([^\s*\x60~\[\]]+)` A tag is `@` followed by word characters, excluding: - Whitespace - Asterisks `*` - Backticks `` ` `` - Tildes `~` - Brackets `[]` **Examples of valid tags:** - `@Task`, `@Done`, `@Waiting` - `@Timesheet`, `@Break` - `@ProjectName`, `@Client-ABC` ### R2: Marker vs Tag Distinction The extraction MUST distinguish between **markers** and **tags** based on their position within a block: | Type | Position | Purpose | |------|----------|---------| | **Marker** | Before any non-whitespace content | Semantic classification (triggers shard creation) | | **Tag** | After non-whitespace content | Metadata annotation (does not trigger shard creation) | **Example:** ```markdown @Task @Streamd Working on feature Some text here @CompletedFeature ``` ### R3: Marker Boundary Tracking The extraction algorithm MUST track a "marker boundary" state: 1. Start with `marker_boundary_encountered = false` 2. While processing tokens: - If whitespace-only: continue (boundary not crossed) - If `@Tag` token found AND boundary NOT crossed: add to markers - If `@Tag` token found AND boundary crossed: add to tags - If any non-whitespace content found: set boundary = crossed ### R4: Nested Token Handling Tag extraction MUST handle nested markdown formatting: - Emphasis: `*@Tag*` or `_@Tag_` - Strong: `**@Tag**` or `__@Tag__` - Strikethrough: `~~@Tag~~` - Links: `[@Tag](url)` Tags inside these formatting elements are still valid and should be extracted. ### R5: Applicable Block Types Tag extraction applies to: - Headings (`# Heading with @Tag`) - Paragraphs (`@Tag in paragraph`) - Quoute Blocks (`> @Tag in Quote`) - List items (each item can have its own markers) --- ## Parsing Logic ### R6: Heading-Based Hierarchy The parser MUST create a hierarchical shard structure based on markdown headings. **Algorithm for determining split level:** 1. Find the minimum heading level that either: - Appears 2+ times in the block list, OR - Has markers AND is not the first heading 2. If no such level exists, do not split (return None) **Example:** ```markdown # Main Title Content here ## Section A Section A content ## Section B Section B content ``` ### R7: List Item Shard Creation Each list item with markers MUST become its own shard: ```markdown - @Task Item one - @Task Item two - Item three ``` ### R8: Shard Simplification When building shards, apply this optimization: - If a shard has exactly 1 child AND no markers AND no tags - Return the child directly instead of wrapping it --- ## Dimension Placement Logic ### R9: Dimension Configuration A **Dimension** defines a classification axis: ``` Dimension { display_name: String // For UI display comment: String? // Documentation propagate: bool // Whether children inherit this dimension } ``` ### R10: Marker Configuration A **Marker** defines how a tag affects dimension placement: ``` Marker { display_name: String placements: [MarkerPlacement] } MarkerPlacement { if_with: Set // Conditional: only apply if ALL these markers present dimension: String // Target dimension name value: String? // Value to assign (defaults to marker name) overwrites: bool // Can overwrite existing placement } ``` ### R11: Conditional Placement Placements with `if_with` conditions MUST only apply when ALL specified markers are present on the same shard. **Example Configuration:** ``` Marker "Task" { placements: [ { dimension: "task", value: "open" }, { if_with: ["Done"], dimension: "task", value: "done" }, { if_with: ["Waiting"], dimension: "task", value: "waiting" }, ] } ``` **Behavior:** - `@Task` alone → `task: "open"` - `@Task @Done` → `task: "done"` (conditional overrides default) - `@Task @Waiting` → `task: "waiting"` ### R12: Localization Algorithm The localization process MUST follow this algorithm: ``` function localize_shard(shard, config, propagated_from_parent, moment): position = copy(propagated_from_parent) // Start with inherited private_position = {} // Non-propagating dimensions for marker in shard.markers: if marker in config.markers: for placement in marker.placements: // Check conditional if placement.if_with is subset of shard.markers: dimension = config.dimensions[placement.dimension] value = placement.value OR marker // Check if we can apply this placement target = dimension.propagate ? position : private_position if placement.dimension not in target OR placement.overwrites: target[placement.dimension] = value // Recursively localize children with propagating dimensions children = [ localize_shard(child, config, position, moment) for child in shard.children ] // Merge private dimensions into final position position.update(private_position) return LocalizedShard( markers: shard.markers, tags: shard.tags, location: position, moment: moment, children: children, ) ``` ### R13: Dimension Propagation When `propagate = true`: - Children inherit the dimension value from their parent - Child can override with their own placement When `propagate = false`: - Dimension value is NOT inherited by children - Each shard must have its own marker to be placed in this dimension **Example:** ``` dimensions: { "project": { propagate: true }, // Children inherit project "task": { propagate: false }, // Each task is independent } ``` ```markdown # @Project-X ## @Task Item A ### Sub-item ## @Task Item B ``` ### R14: Overwrite Behavior Default: A placement does NOT overwrite an existing value in the dimension. With `overwrites: true`: The placement WILL replace any existing value. This allows conditional placements to override base placements. --- ## File Naming Convention ### R15: File Name Format Files follow the pattern: `YYYYMMDD-HHMMSS [markers].md` - `YYYYMMDD`: Date (8 digits, required) - `HHMMSS`: Time (4-6 digits, optional, pads with zeros) - `[markers]`: Space-separated marker names extracted from file content **Extraction regex:** `^(?P\d{8})(?:-(?P