diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..2371a49 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + push: + branches: + - main + - planning + pull_request: + branches: + - main + - planning + +jobs: + rustfmt: + name: Format Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt -- --check + + clippy: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - run: cargo clippy --all-targets --all-features -- -D warnings + + test: + name: Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo test --verbose diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 56a3d77..3e710e1 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -46,12 +46,16 @@ Current product definition: - Core project structure includes materials/ingredients, required tools, and ordered step-by-step instructions. - Steps may include embedded media hosted on this instance or linked from external sources. - Projects may include external canonical links (for example homepage, repository, or source publication). +- Projects may include multiple typed canonical links (for example homepage, repository, build instructions, video walkthrough, or source publication). Canonical links are project-scoped by default and remain stable across revisions; version-scoped links are allowed when a specific published revision needs its own artifact or source reference. - Explicit project versioning is preferred and will be part of the domain model. - The project model is composable: a minimal core plus optional domain-specific extensions. - Domain-specific detail (for example knitting patterns/yarns, 3D print profiles/STLs, electronics BoM data) should be representable without being mandatory for all instances. - First-party FeDIY focuses on a stable extension mechanism rather than implementing every niche schema directly. -- Materials are also an extensible entity: the core material record captures display name, quantity, and unit; domain-specific attributes (yarn weight, fibre content, filament diameter/material, wood species/grade, electronics component value/package) are carried in extension payloads on the material entry, using the same extension mechanism as project-level extensions. +- Materials are project-scoped entries with a fixed core shape: display name, structured quantity, unit, and optional note. Material entries are embedded within a project revision rather than modeled as independent top-level entities in Phase 1. Each material entry can carry the same extension payload mechanism as project-level extensions for domain-specific attributes (yarn weight, fibre content, filament diameter/material, wood species/grade, electronics component value/package). +- Material extensions use the same typed JSON block model as project extensions: namespace, type identifier, version, and payload. Clients discover available material extensions from explicit material-entry metadata. Known blocks are schema-validated; unknown optional blocks are preserved and round-tripped; unknown required blocks can be rejected until the instance supports them. +- Required tools are project-scoped entries with a lightweight core shape: display name and optional note. Tools are embedded within a project revision rather than modeled as independent top-level entities in Phase 1. Each tool entry can carry optional metadata such as skill level, safety notes, and alternatives. A shared tool taxonomy is a future option, not a Phase 1 requirement. - A federated material catalog is a long-term goal: community-defined material types and shared taxonomy entries could be federated as ActivityPub objects, allowing instances to reference a common vocabulary without requiring central authority. +- Project-level extensions are stored as typed JSON blocks on the revision, each with a namespace, type identifier, version, and payload. Namespaces should be collision-resistant (for example reverse-DNS or URL-based identifiers). Clients discover available extensions from explicit revision metadata. Known extension blocks are schema-validated; unknown optional blocks are preserved and round-tripped; unknown required blocks can be rejected until the instance supports them. ### Project Revision Lifecycle diff --git a/docs/OPEN_QUESTIONS.md b/docs/OPEN_QUESTIONS.md index b0de403..be5dae5 100644 --- a/docs/OPEN_QUESTIONS.md +++ b/docs/OPEN_QUESTIONS.md @@ -13,11 +13,13 @@ Each question is tagged with the phase it blocks or most affects: [P0], [P1], [P - Explicit project versioning is preferred and should be supported. - A project is composable: FeDIY provides a minimal core model, and instances can tailor project detail via optional domain-specific extensions. - FeDIY should not require first-party implementation of every domain detail up front; expert communities can define richer schemas over time. -- Materials are also extensible entities. The core material record (name, quantity, unit) can carry domain-specific extension payloads using the same mechanism as project extensions. Community-defined material type schemas (e.g. yarn, filament, PCB component) can be layered on without modifying the core model. +- Materials are project-scoped entries with a fixed core shape: display name, structured quantity, unit, and optional note. Material entries are embedded within a project revision rather than modeled as independent top-level entities in Phase 1. Each material entry can carry the same extension payload mechanism as project extensions. Community-defined material type schemas (e.g. yarn, filament, PCB component) can be layered on without modifying the core model. +- Required tools are project-scoped entries with a lightweight core shape: display name and optional note. Tools are embedded within a project revision rather than modeled as independent top-level entities in Phase 1. Each tool entry can carry optional metadata such as skill level, safety notes, and alternatives. A shared tool taxonomy remains a future option, not a Phase 1 requirement. - **Database: PostgreSQL is the primary persistence target.** JSONB, native full-text search, transactional consistency under concurrent federation fan-in, and broad managed hosting support make it the clear long-term fit. The persistence layer is behind a repository abstraction (trait-based interfaces), which keeps business logic independent of the database driver and leaves SQLite viable as a future lightweight self-hosting option without requiring changes to domain logic. See [ADR TBD: Persistence Layer Architecture]. - **Baseline content prohibitions (hardcoded, not operator-configurable):** CSAM, doxxing, and non-consensual intimate imagery (NCII) are prohibited on any FeDIY instance regardless of operator policy. The guiding principle is **consent**: minors cannot consent to sexual content; individuals have not consented to having their private identifying information published; subjects of intimate imagery have not consented to its distribution. Enforcement is in-code as far as technically feasible (hash-matching for CSAM and NCII; upload-time pattern detection and mandatory human-review tooling for doxxing). Weapons, violence, and similar dual-use content are **not** hardcoded prohibitions — legitimate DIY projects (fireworks, blacksmithing, knife-making) are indistinguishable at the software level and are handled by operator content policy and community moderation. - **Personal data register (Q38):** Full register in ARCHITECTURE.md. Required registration fields: email, password hash, handle, display name, minimum-age-verified boolean (raw DOB discarded after age check). IP addresses never stored — ephemeral only. Optional profile fields (bio, avatar, header image, location, preferred crafts, pronouns, external links, locale, display preferences) all under contract. Analytics must be truly aggregate/anonymised — per-user event streams require consent. Handles are changeable with a redirect from old to new URL. - **Project revision model (Q1):** Use immutable numbered revisions plus mutable drafts. Projects may reference other projects as reusable sub-processes or prerequisites, but recursive content is represented as a graph of project references rather than inline embedding of entire project bodies. Each publish action snapshots materials, tools, steps, media references, canonical links, extension payloads, and any project references as-of publish time. Full history is visible in the API and UI, with the latest published revision as the default view. +- **Federated material catalogs and shared vocabularies (Q2e):** Deferred to Phase 2/P3. Federated material type catalogs, tool registries, and other shared domain vocabularies as ActivityPub objects are a future option enabled by the current extension model, not a Phase 1 requirement. The typed JSON extension block mechanism (Q2c/Q2d) is designed to accommodate future federated catalog entries without requiring core schema changes. Authorization, governance, and RTBF semantics for federated community-curated catalogs will be explored in Phase 2+. ## Upfront Clarification Plan (P0 -> Early P1) @@ -55,49 +57,35 @@ The goal is to remove ambiguity before implementation while keeping scope realis Decision: immutable numbered revisions plus mutable drafts. Projects may reference other projects as reusable sub-processes or prerequisites, but recursive content is represented as a graph of project references rather than inline embedding of entire project bodies. Publishing freezes a revision snapshot; later edits create a new draft and then a new numbered revision. Full history is visible in API and UI, with the latest published revision as the default view. -**Q2 [P1]** How are materials modelled? +**Q2 [P1]** ~~How are materials modelled?~~ **RESOLVED — see Resolved Decisions and ARCHITECTURE.md Project Domain Baseline.** -- Free-text only, or linked to a shared taxonomy/catalog? -- Do quantities and units need to be structured (for search/filtering), or is prose sufficient for MVP? -- Are materials solely inline entries within a project, or are they also independent top-level entities that can be referenced by multiple projects? -- Does the core material record have a fixed set of fields (name, quantity, unit, optional note) with extension payloads carrying domain-specific attributes? Or is the entire material record opaque beyond a display name? -- How are domain-specific material extensions (yarn weight/fibre/colourway, filament diameter/material/temperature profile, wood species/grade/finish, electronics component value/tolerance/package) represented? Same extension payload shape as project extensions, or a separate material-type schema? -- At what phase should structured quantity/unit data be required? A unit normalisation scheme (SI base + common craft units) would enable cross-instance filtering and quantity scaling; is that P1 or later? +Decision: project-scoped material entries with a fixed core shape: display name, structured quantity, unit, and optional note. Material entries are embedded within a project revision rather than modeled as independent top-level entities in Phase 1. Each material entry can carry the same extension payload mechanism as project extensions. Community-defined material type schemas can be layered on without modifying the core model. **Q2d [P1/P2]** What is the extension model for domain-specific material data? -- Does a material entry carry the same extension payload mechanism as a project (namespaced typed block, discoverable schema)? -- Who can define a material type schema — instance operators only, or any community contributor who publishes a schema at a well-known URL? -- Should FeDIY ship a small set of reference material type schemas (yarn, 3D filament, electronic component) as non-mandatory examples to establish the pattern? -- How does a client know which material type extension(s) a given entry carries, and how does it fall back gracefully when it does not support the type? -- What is the relationship between project-level extensions and material-level extensions? Can a domain extension define both a project schema and a material schema that are versioned together? +~~Does a material entry carry the same extension payload mechanism as a project (namespaced typed block, discoverable schema)?~~ ~~Who can define a material type schema — instance operators only, or any community contributor who publishes a schema at a well-known URL?~~ ~~Should FeDIY ship a small set of reference material type schemas (yarn, 3D filament, electronic component) as non-mandatory examples to establish the pattern?~~ ~~How does a client know which material type extension(s) a given entry carries, and how does it fall back gracefully when it does not support the type?~~ ~~What is the relationship between project-level extensions and material-level extensions? Can a domain extension define both a project schema and a material schema that are versioned together?~~ -**Q2e [P2/P3]** What would a federated material catalog look like? +Decision: yes - material entries use the same typed JSON extension block model as projects. Each material extension block has a namespace, type identifier, version, and payload; clients discover them from explicit material-entry metadata and preserve unknown optional blocks opaquely. Material type schemas may be published by instance operators or the community, and a shared material taxonomy remains a future option. A domain extension may define both a project schema and a material schema, versioned separately but named consistently. -- Could shared material types (e.g. a yarn colourway catalog, a filament brand/profile registry) be published as ActivityPub objects and federated between instances? -- What ActivityPub object type would a material catalog entry use? A custom `fediy:MaterialType` in a FeDIY JSON-LD context, or an existing vocabulary term? -- Who is authoritative for a shared catalog entry — the originating instance, a designated community instance, or a multi-instance governance process? -- How does a material catalog entry interact with the right to erasure / RTBF? If a community-curated entry (not personal data) is federated, different deletion semantics apply than for user personal data. -- Is a federated catalog in scope before federation is live (Phase 2), or does it only make sense alongside AP federation? +**Q2e [P2/P3]** What would a federated material catalog look like? **RESOLVED — see Resolved Decisions above.** **Q2a [P1]** How are required tools modelled? -- Free-text tools list only, or a normalized/tool taxonomy strategy? -- Should tools support optional metadata (skill level, safety notes, alternatives)? +~~Free-text tools list only, or a normalized/tool taxonomy strategy?~~ ~~Should tools support optional metadata (skill level, safety notes, alternatives)?~~ + +Decision: project-scoped tool entries with a lightweight core shape: display name and optional note. Tools are embedded within a project revision rather than modeled as independent top-level entities in Phase 1. Each tool entry can carry optional metadata such as skill level, safety notes, and alternatives. A shared tool taxonomy remains a future option, not a Phase 1 requirement. **Q2b [P1]** How are canonical external links modelled and validated? -- Single canonical link or multiple typed links (homepage, repository, video, reference)? -- Are external links version-scoped or project-scoped? -- What URL validation and safety checks are required? +~~Single canonical link or multiple typed links (homepage, repository, video, reference)?~~ ~~Are external links version-scoped or project-scoped?~~ ~~What URL validation and safety checks are required?~~ + +Decision: multiple typed project-scoped links. A project can carry several canonical links such as homepage, source repository, build instructions, video walkthrough, and reference material. Links are project-scoped by default so they remain stable across revisions; a version-scoped link is allowed when a specific published revision needs its own source or artifact reference. Validation requires an allowed URL scheme, scheme-specific safety checks, and rejection of dangerous or nonsensical schemes. **Q2c [P1/P2]** What is the extension model for domain-specific project data? -- What extension shape is supported first: typed JSON blocks, namespaced key/value fields, or plugin-defined schemas? -- How are extensions identified and namespaced to avoid collisions across instances? -- How do API and UI clients discover which extensions are present on a project? -- Which extension fields are indexed for search, and which remain opaque? -- What validation guarantees does the server provide for extension payloads? +~~What extension shape is supported first: typed JSON blocks, namespaced key/value fields, or plugin-defined schemas?~~ ~~How are extensions identified and namespaced to avoid collisions across instances?~~ ~~How do API and UI clients discover which extensions are present on a project?~~ ~~Which extension fields are indexed for search, and which remain opaque?~~ ~~What validation guarantees does the server provide for extension payloads?~~ + +Decision: typed JSON extension blocks attached to a project revision. Each block has a stable namespace, a type identifier, a version, and a payload. Namespaces are collision-resistant (for example reverse-DNS or URL-based identifiers). API and UI clients discover extensions via explicit metadata on the project revision. Known extensions are validated against their schema; unknown optional extensions are preserved opaquely and round-trip through the API; unknown required extensions may be rejected until the instance supports them. **Q3 [P1]** What is the tag and category strategy? diff --git a/docs/README.md b/docs/README.md index 7979d49..4d9f436 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ This directory contains product and technical planning documents for FeDIY. - [Roadmap](ROADMAP.md): phased milestones from MVP to broader federation support. - [Architecture](ARCHITECTURE.md): system boundaries, components, and design constraints. -- [Workflow](WORKFLOW.md): Red-Green TDD/BDD delivery model and GitHub Flow branching policy. +- [Workflow](WORKFLOW.md): Red-Green TDD/BDD delivery model and feature-branch workflow with pull request review. - [Open Questions](OPEN_QUESTIONS.md): unresolved design and product decisions, tagged by phase. - [ADRs](adrs/): architecture decision records. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 51d2a25..8eb86e7 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -12,7 +12,7 @@ Goals: - Stabilize development environment and reproducible tooling, while keeping the contributor path usable for people who do not use Nix. - Define domain language and baseline architecture docs. -- Establish quality process (TDD/BDD + GitHub Flow). +- Establish quality process (TDD/BDD + feature-branch workflow with pull request review). - Clarify the core-plus-extension project contract before implementation. - Define personal data categories, retention windows, and erasure obligations before any user data model is implemented. @@ -20,7 +20,7 @@ Exit criteria: - Agreed glossary and architecture baseline. - Documented contribution and branch workflow. -- CI skeleton in place for formatting, linting, and tests. +- Gitea Actions CI pipeline in place for formatting, linting, and tests. - Documented onboarding path for non-Nix contributors using standard Rust toolchain commands. - ADR for project revision lifecycle (draft/publish/supersede). - ADR for composable extension mechanism (shape, namespacing, discovery). diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index d0eb4c7..0f55293 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -1,6 +1,6 @@ # Development Workflow -This project uses Red-Green TDD/BDD and GitHub Flow. +This project uses Red-Green TDD/BDD and feature-branch workflow with pull request review. ## Red-Green TDD/BDD Cycle @@ -23,7 +23,7 @@ Definition of done for a change: - Refactoring has preserved behavior. - Documentation is updated when contracts or workflows change. -## GitHub Flow Branching Policy +## Feature-Branch Workflow Use short-lived branches from main.