Files
RuDS/specs/script.allium
2026-04-09 17:46:34 +02:00

166 lines
4.8 KiB
Plaintext

-- allium: 1
-- bDS Scripting System
-- Scope: core (Wave 6 — Lua runtime and scripting docs)
-- Distilled from: src/main/engine/ScriptEngine.ts, schema.ts
-- Scripting runtime is Lua (mlua crate). Behavioural contract preserved.
entity Script {
slug: String
title: String
kind: macro | utility | transform
entrypoint: String -- default: "render" for macros
enabled: Boolean
status: draft | published
content: String?
version: Integer
file_path: String
created_at: Timestamp
updated_at: Timestamp
-- Derived
content_location: if status = published: file_path else: content
transitions status {
draft -> published
published -> draft
}
}
surface ScriptSurface {
context script: Script
exposes:
script.slug
script.title
script.kind
script.entrypoint
script.enabled
script.status
script.content when script.content != null
script.version
script.file_path
script.created_at
script.updated_at
script.content_location
}
surface ScriptManagementSurface {
facing _: ScriptOperator
provides:
CreateScriptRequested(title, kind, content, entrypoint)
CreateAndPublishScriptRequested(title, kind, content, entrypoint)
PublishScriptRequested(script)
DeleteScriptRequested(script)
RunUtilityRequested(script)
MacroExpansionRequested(script, template_context)
BlogmarkReceived(data)
RebuildScriptsFromFilesRequested(project)
}
invariant UniqueScriptSlug {
for a in Scripts:
for b in Scripts:
a != b implies a.slug != b.slug
}
invariant ScriptFileLayout {
for s in Scripts where file_path != "":
s.file_path = format("scripts/{slug}.lua", slug: s.slug)
}
-- Script files use .lua with standard --- YAML frontmatter
rule CreateScript {
when: CreateScriptRequested(title, kind, content, entrypoint)
let slug = slugify(title)
-- Creates a draft script: content stored in DB, no file written yet
ensures: Script.created(
slug: slug,
title: title,
kind: kind,
content: content,
entrypoint: entrypoint ?? "render",
status: draft,
enabled: true,
version: 1,
file_path: ""
)
}
rule CreateAndPublishScript {
-- Alternative creation path: create + immediately publish (file written)
-- TypeScript: createScript() does this in one step
when: CreateAndPublishScriptRequested(title, kind, content, entrypoint)
let slug = slugify(title)
requires: ValidateScript(content) = valid
ensures:
let new_script = Script.created(
slug: slug,
title: title,
kind: kind,
content: null,
entrypoint: entrypoint ?? "render",
status: published,
enabled: true,
version: 1,
file_path: format("scripts/{slug}.lua", slug: slug)
)
ScriptFileWritten(new_script)
}
rule PublishScript {
when: PublishScriptRequested(script)
requires: ValidateScript(script.content) = valid
-- AST parsing must succeed
ensures: script.status = published
ensures: ScriptFileWritten(script)
ensures: script.content = null
}
rule DeleteScript {
when: DeleteScriptRequested(script)
ensures: not exists script
ensures: ScriptFileDeleted(script)
}
-- Script execution contracts by kind
rule ExecuteMacro {
when: MacroExpansionRequested(script, template_context)
requires: script.kind = macro
requires: script.enabled = true
-- Macro scripts are invoked during template rendering
-- via [[slug param1=value1 param2=value2]] syntax in post content
-- They receive named parameters and the template context, return HTML
ensures: MacroOutputProduced(script, html_output)
}
rule ExecuteUtility {
when: RunUtilityRequested(script)
requires: script.kind = utility
requires: script.enabled = true
-- Runs on-demand from the UI, produces stdout output
ensures: UtilityOutputProduced(script, stdout)
}
rule ExecuteTransform {
when: BlogmarkReceived(data)
-- Transform scripts run sequentially on blogmark deep link data
-- Input: title, content, tags, categories, source url
-- Each transform can modify the data before post creation
let transforms = Scripts where kind = transform and enabled = true
for t in ordered_by(transforms, s => s.slug):
ensures: TransformApplied(t, data)
@guidance
-- bds://new-post deep links from browser bookmarks
-- Max 5 toast notifications per script, 20 total
}
rule RebuildScriptsFromFiles {
when: RebuildScriptsFromFilesRequested(project)
for file in scan_directory(project.effective_data_dir + "/scripts", "*.lua"):
let parsed = parse_script_file(file)
ensures: Script.created(parsed)
}