Skip to content

Domain Model

fushinryu-model is built around entity types that form a containment hierarchy.

graph TD
    S[Scope] -->|contains| US[UserStory]
    S -->|backlog| T[Task]
    S -->|rule_categories| CAT[Category]
    CAT -->|subcategories| CAT
    CAT -->|ruleset| RS[Ruleset]
    RS --> CMD[Commandment]
    RS --> SUG[Suggestion]
    US -->|contains| AC[AcceptanceCriterion]
    AC -->|contains| V[ValidationEntry\nManualValidation · AutomatedValidation]
    T -. references .-> AC
    S -->|roles| R[Role]
    R -->|parent| R
    R -->|competences| RC[RoleCompetence]
    RC --> C[Competence]
    S -->|role_assignments| RA[RoleAssignment]
    RA --> EMP[Employee]
    RA --> R
    EMP -->|competences| EC[EmployeeCompetence]
    EC --> C
    TP[TrainingProgram] -->|competences| PC[ProgramCompetence]
    PC --> C
    RT[ReceivedTraining] --> TP
    RT --> EMP
    RT --> TR[Trainer]

Scope

A Scope is the top-level organisational unit. It groups related UserStory instances, Task work items, and rule categories under a named, identifiable container. Scopes can declare parent scopes, forming a directed acyclic graph (DAG) that supports inheritance — see Scope Hierarchy.

Key constraints

  • id must follow programming identifier rules: start with a letter or _letter, followed by letters, digits, or underscores.
  • User story id values must be unique within a scope.
  • Task id values must be unique within the scope's backlog.
  • description is optional free-form text; it is inherited from parent scopes when absent.
  • Category name values must be unique within rule_categories.

UserStory

A UserStory captures a high-level requirement from the perspective of a role using the classic As a / I want / so that structure, stored as who, what, and why fields.

Stories are classified as either functional or technical via the UserStoryType enum.

The optional dod field holds a free-form definition of done. The tags field is an unordered set of short label strings (frozenset[str]) used for categorisation; tags from parent and child stories are merged as a union.

Key constraints

  • Acceptance criterion id values must be unique within a story.
  • A story is considered validated (is_validated = True) only when it is active, has at least one active acceptance criterion, and all active criteria are validated.

Task

A Task represents a concrete work item assigned to a scope's backlog. It carries a title, a full description, a priority, a kind, and a lifecycle_status. The on_hold flag marks a task as temporarily blocked without changing its lifecycle state.

The related_acs field is a frozenset[str] of acceptance criterion references (e.g. "AC1.2") that the task addresses. It merges as a union across the scope hierarchy, the same way UserStory.tags does.

Key constraints

Field Constraint
id Integer, unique within the enclosing scope's backlog
estimated_duration Required for enhancement, bug, documentation, release, deployment
related_acs String references to AC ids; merged as a union

AcceptanceCriterion

An AcceptanceCriterion encodes the Given / When / Then structure of a testable condition.

Field Required Description
id yes Integer, unique within the enclosing story
then yes The expected outcome
given no Initial context or state
when no Trigger condition
tags no Unordered set of short label strings; merged as union
requirement_specification no ISO 29148 document type (SRS, SyRS, or StRS)
requirement_group no Requirement category within the specification

When both classification fields are provided, requirement_group must be valid for the chosen requirement_specification. Setting requirement_group without requirement_specification raises a validation error.

A criterion is validated (is_validated = True) when its validations set is non-empty and every record has passed=True.

ValidationEntry

Validation evidence is stored as a discriminated union of two frozen record types.

ManualValidation

Recorded by a human reviewer. Requires a verdict string explaining why the criterion passed or failed.

AutomatedValidation

Linked to an automated test or check. Identified by source (the file or module) and name (the test name). During merges, automated validations are deduplicated by (source, name), keeping the most recent timestamp.

Rule Categories

A Scope carries a rule_categories field — a frozenset[Category] that represents the hatto (法度): the set of scoped policy rules that govern the scope.

Category

A Category groups Commandment and Suggestion instances that apply under a stated condition. Key fields:

Field Required Description
name yes Identifier-pattern string; unique within its parent's subcategories
applicability yes Free-form description of when the category's rules apply
tags no Short label strings; defaults to empty frozenset
additive no Controls suggestion merge behaviour (see Merging); defaults to False
ruleset no The category's Ruleset; defaults to empty
subcategories no Nested child categories; merged by name when scopes are merged

Categories form a tree. The effective_commandments and effective_suggestions properties return the union of a category's own rules and all its subcategories' rules, recursively.

Commandment and Suggestion

Both are frozen value objects with a single body: str field. A non-empty, non-whitespace body is required.

  • Commandment — must be honoured without exception.
  • Suggestion — should be honoured; may be waived under justified conditions.

Ruleset

A Ruleset holds two frozensets: commandments and suggestions. Both default to empty.

Roles and Workforce

A Scope carries two workforce-related fields: roles and role_assignments.

Role

A Role is a named actor within a scope. Key fields:

Field Required Description
name yes Identifier-pattern string; unique within Scope.roles
description yes Non-empty description of the role's responsibilities
parent no The role this one is derived from; defaults to None
competences no frozenset[RoleCompetence]; competence names must be unique

The flatten() method returns a copy of the role with parent=None, resolving the full chain. Scope.collapse() calls flatten() on every role automatically.

When two scopes are merged, roles sharing the same name are implicitly linked: the child scope's role receives the parent scope's role as its parent, building an explicit hierarchy across scope levels.

Competence

Competence is a pure value object identified by the (area, category, name) triple. Two instances with identical fields are considered equal.

CompetenceLevel is an ordered int enum (NONE=0, BASIC=1, MODERATE=2, PROFICIENT=3) representing proficiency. CompetenceRelevance is an ordered int enum (UNRELATED=0, RELEVANT=1, DESIRABLE=2, KEY=4) acting as a multiplier in weighted computations.

RoleCompetence binds a Competence to a role:

Field Description
competence The skill being bound
relevance CompetenceRelevance — how important this skill is for the role
proficiency_threshold CompetenceLevel — minimum required level

When parent and child scope roles are merged, RoleCompetence bindings use max-value semantics: the independently higher relevance and proficiency threshold from either side are kept.

Employee

An Employee represents a person in the system.

Field Description
id Non-empty string; unique identifier (e.g. "EMP-001")
username Non-empty login name
competences frozenset[EmployeeCompetence]; each competence at most once

EmployeeCompetence binds a Competence to the employee's actual CompetenceLevel.

RoleAssignment, TrainingNeeds, and idoneity

A RoleAssignment links an Employee to a Role and exposes two derived metrics.

training_needs() -> TrainingNeeds — computes the per-competence gap between the employee's profile and the role's requirements. For each RoleCompetence on the role:

  • Looks up the employee's current level. Missing competences default to NONE.
  • Produces a TrainingNeed with partial_distance = max(0, threshold - level) × relevance.

TrainingNeeds.total_distance sums all partial_distance values. Zero means the employee meets every threshold; higher values indicate greater training demand.

idoneity — weighted average suitability score:

idoneity = Σ(level × relevance) / Σ(relevance)

Only non-UNRELATED competences contribute. Returns 0.0 when no relevant competences are defined on the role. The maximum value equals CompetenceLevel.PROFICIENT (3.0).

Scope.role_assignments is a frozenset[RoleAssignment]. When scopes are merged it follows child-override semantics keyed by (employee.id, role.name).

Training

TrainingProgram and ReceivedTraining are standalone entities — they are never attached to a Scope collection and carry no merge semantics.

TrainingProgram

Represents an available training course.

Field Required Description
id yes Integer identifier
title yes Non-empty course title
syllabus yes Non-empty description of course content
duration yes Positive timedelta
competences no frozenset[ProgramCompetence]; each competence at most once; defaults to empty

ProgramCompetence

Binds a Competence to the CompetenceLevel at which it is taught by the program.

Field Description
competence The skill covered by the program
level CompetenceLevel — proficiency level taught

Trainer

Value object identifying who delivered a training session.

Field Required Description
name yes Non-empty name of the trainer
organization no Employing organisation; defaults to None

ReceivedTraining

Records that an employee completed a training program.

Field Required Description
program yes The TrainingProgram completed
employee yes The Employee who attended
received_on yes datetime.date of completion
location yes Non-empty location where training took place
trainer yes The Trainer who delivered the session
evidences no frozenset[str] of certificate URLs or proof references; defaults to empty

The active flag

All three entity types carry an active: bool field (default True). Setting it to False performs a soft deletion — the entity is retained in the data but excluded from validation checks. An inactive UserStory implicitly deactivates all its acceptance criteria for validation purposes, regardless of their individual active values.