Files
bDS2/specs/project.allium

114 lines
3.0 KiB
Plaintext

-- allium: 1
-- bDS Project Management
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/ProjectEngine.ts, schema.ts
surface ProjectControlSurface {
facing _: ProjectOperator
provides:
CreateProjectRequested(name, data_path)
SetActiveProjectRequested(project)
DeleteProjectRequested(project)
}
entity Project {
name: String
slug: String
description: String?
data_path: String?
is_active: Boolean
created_at: Timestamp
updated_at: Timestamp
-- Relationships
posts: Post with project = this
media: Media with project = this
tags: Tag with project = this
-- Derived
internal_base_dir: String
-- {user_data}/projects/{id}/
-- Contains: meta/, thumbnails/, tags.json
effective_data_dir: data_path ?? internal_base_dir
-- Custom data path overrides default
}
surface ProjectSurface {
context project: Project
exposes:
project.name
project.slug
project.description when project.description != null
project.data_path when project.data_path != null
project.is_active
project.created_at
project.updated_at
project.posts.count
project.media.count
project.tags.count
project.internal_base_dir
project.effective_data_dir
}
invariant SingleActiveProject {
-- Exactly one project is active at any time
let active = Projects where is_active
active.count = 1
}
invariant UniqueProjectSlug {
for a in Projects:
for b in Projects:
a != b implies a.slug != b.slug
}
rule CreateProject {
when: CreateProjectRequested(name, data_path)
let slug = slugify(name)
ensures: Project.created(
name: name,
slug: slug,
data_path: data_path,
is_active: false
)
}
invariant ProjectTemplatesDirectoryReservedForUserTemplates {
-- The project templates directory stores only user-managed templates.
-- Creating a project does not populate effective_data_dir/templates with bundled defaults.
}
rule SetActiveProject {
when: SetActiveProjectRequested(project)
let previous = Projects where is_active = true
ensures:
for p in previous:
p.is_active = false
ensures: project.is_active = true
}
rule DeleteProject {
when: DeleteProjectRequested(project)
requires: project.id != "default"
-- The default project (id='default') cannot be deleted
requires: project.is_active = false
-- The currently active project cannot be deleted
ensures: not exists project
@guidance
-- deleteProjectWithData removes DB rows + internal directory
-- but preserves external data at custom data_path
}
config {
default_project_id: String = "default"
default_project_name: String = "My Blog"
}
invariant DefaultProjectExists {
-- A project with id='default' always exists
-- It is created on first launch if missing
exists p in Projects where p.id = "default"
}