Changelog
All notable changes to this project will be documented in this file.
The format follows Keep a Changelog and this project adheres to Semantic Versioning.
[0.4.0] - 2026-07-04
Added
Task.active_duration— time actively spent working on the task, distinct fromactual_duration(which may include background or automated processing time). If only one ofactive_duration/actual_durationis provided at instantiation, the other defaults to the same value.Risk— a project risk attached toScope.risks(frozenset[Risk], keyed by integerid, merged likebacklog/user_stories). Fields:id,description,rasci(frozenset[RasciAssignment], responsibility for managing the risk overall),potential_assessmentandresidual_assessment(RiskAssessment, before/after controls),risk_controls(frozenset[RiskControl], accumulated by union on merge).RiskAssessment— scoresimpact,probability, andthreatas[0.0, 1.0]indexes and categorizes each into astrlabel via a pluggableRiskAssessmentStrategy. Carries thestrategyinstance used plus a computedstrategy_namefield for provenance.RiskAssessment.assess(impact, probability, threat, strategy=...)is the convenience factory.RiskAssessmentStrategy— abstract base (frozenpydantic.BaseModel+ABC) withcategorize_impact/categorize_probability/categorize_threatmethods.ThresholdRiskAssessmentStrategyis the default implementation, splitting[0.0, 1.0]into four quartile buckets.RiskLevel,ImpactLevel,ProbabilityLevel— 4-level, semantically ordered enums used as the default strategy's category vocabulary for threat, impact, and probability respectively.RiskControl— a control measure with adescriptionand an optional embeddedprocedure: Procedure | None.Risk,RiskAssessment,RiskAssessmentStrategy,ThresholdRiskAssessmentStrategy,DEFAULT_RISK_ASSESSMENT_STRATEGY,RiskControl,RiskLevel,ImpactLevel, andProbabilityLevelare now part of the public API, importable directly fromfushinryu_model.
[0.3.0] - 2026-07-03
Added
DescriptionMode— enum controlling how a childRole's description is composed with its parent's during a scope merge. Values:OVERRIDE(default),EXTEND,PREPEND. Now part of the public API.Role.description_mode— newDescriptionModefield (defaults toOVERRIDE).ChangeKind— enum of change event kinds:CREATED,UPDATED,DEACTIVATED,SUPERSEDED.HistoryEntry— frozen value object recording a change event:kind(ChangeKind), timezone-awaretimestamp, optionalauthor.UserStoryHistoryEntry— extendsHistoryEntrywithadded_acs: frozenset[int]anddeactivated_acs: frozenset[int]for AC-level change traceability.Historized— frozen Pydantic mixin addinghistory: frozenset[HistoryEntry]with union merge semantics.UserStoryoverrides the field type tofrozenset[UserStoryHistoryEntry].Comment— frozen value object withid,body, timezone-awaretimestamp, and optionalauthor.Commentable— frozen Pydantic mixin addingcomments: frozenset[Comment]with union merge semantics.UserStoryandTasknow carry bothHistorizedandCommentable;RolecarriesHistorizedonly.ChangeKind,Comment,Commentable,HistoryEntry,Historized, andUserStoryHistoryEntryare now part of the public API, importable directly fromfushinryu_model.
Changed
Role.descriptionmerge behaviour is now explicit: the child'sdescription_modefield controls composition. BREAKING: the default isOVERRIDE(child replaces parent), replacing the prior implicitEXTENDbehaviour (child appended to parent). Roles that relied on the old accumulation must setdescription_mode=EXTENDexplicitly.
[0.2.0] - 2026-06-20
Added
examples/interactive explorer (uv run python -m examples.tour) walking every feature area of the model through the fictional Windward Systems / BreezeBoard scenario. Repository-only: the wheel now explicitly packagesfushinryu_modelalone.RASCILevel— semantically ordered enum (via the newfields-metadatadependency'sSemanticallyOrderedEnum) for RASCI responsibility levels:ACCOUNTABLE("A") >RESPONSIBLE("R") >SUPPORTIVE("S") >CONSULTED("C") >INFORMED("I"). Now part of the public API.Procedure— non-mergeable flowchart entity attached toScope.procedures. Fields:id(non-empty, the merge key),description(non-empty),nodes(frozenset[ProcedureNode]). Validates unique node ids and that every node's linkage target resolves to a node id present in the same procedure; multiple start terminators are allowed.Node— discriminated union of 11 flowchart node kinds (StartTerminatorNode,EndTerminatorNode,ActivityNode,DecisionNode,InputNode,OutputNode,PredefinedProcedureNode,ManualOperationNode,ManualInputNode,ForkNode,SyncNode), each carryingid,description, andrasci(frozenset[RasciAssignment]).DecisionNode.casespairs case labels with next-node ids (DecisionCase);ForkNode.brancheslists parallel next-node ids;PredefinedProcedureNode.procedure_idreferences anotherProcedure.id.RasciAssignment— binding pairing a role name with aRASCILevel, used byProcedureNode.rasci.Scope.procedures—frozenset[Procedure]field, unique byidwithin a scope. On merge, a child scope'sProcedurereplaces a same-id parentProcedurewholesale (no field- or node-level merging), unlike the rest ofScope's collections.Procedure,ProcedureNode, all 11 node classes,DecisionCase, andRasciAssignmentare now part of the public API, importable directly fromfushinryu_model.
Changed
pleromaandpydanticdependency constraints tightened from>=to~=, matching the project's pinning convention for library dependencies.
[0.1.6] - 2026-06-12
Added
ScopeKind—strenum classifying the type of organisational unit a Scope represents. Values:organization,office,area,department,team,project,development.Scope.kind— optionalScopeKind | Nonefield (defaults toNone). Inherits standard scalar merge semantics: a child's non-Nonekind overrides the parent's; aNonechild kind leaves the parent's value intact.ScopeKindis now part of the public API, importable directly fromfushinryu_model.ProgramCompetence— binding that associates aCompetencewith theCompetenceLevelat which it is taught by a program.TrainingProgram— standalone training course entity. Fields:id(int),title(non-empty),syllabus(non-empty),duration(positivetimedelta),competences(frozenset[ProgramCompetence], defaults to empty). Each competence must appear at most once. Not attached toScope.Trainer— value object identifying who delivered a training. Fields:name(non-empty),organization(str | None, defaults toNone).ReceivedTraining— record that an employee completed a training program. Fields:program,employee,received_on(datetime.date),location(non-empty),trainer,evidences(frozenset[str], defaults to empty). Not attached toScope.ProgramCompetence,TrainingProgram,Trainer, andReceivedTrainingare now part of the public API, importable directly fromfushinryu_model.
[0.1.5] - 2026-06-12
Added
Role— named actor entity. Fields:name(identifier-pattern validated),description(non-empty),parent(Role | None, defaults toNone),competences(frozenset[RoleCompetence], defaults to empty). Exposesflatten()method that resolves and removes the parent chain. Competence names within a role must be unique.CompetenceLevel— orderedintenum with valuesNONE=0,BASIC=1,MODERATE=2,PROFICIENT=3. Integer values enable direct comparison andmax()in merge operations.CompetenceRelevance— orderedintenum with valuesUNRELATED=0,RELEVANT=1,DESIRABLE=2,KEY=4. Integer values act as multipliers in weighted computations.Competence— value object identified by(area, category, name)triple. All three fields must be non-empty strings.RoleCompetence— binding that associates aCompetencewith arelevanceandproficiency_thresholdon a role. Merges using max-value semantics: the higher relevance and the higher proficiency threshold from parent/child are kept independently.Scope.roles—frozenset[Role]field. Role names must be unique within the set. Defaults to empty. Merges by matchingRole.name: same-named roles from parent and child scopes are linked (child'sparentis set to the parent scope's role).collapse()flattens all role parent chains.EmployeeCompetence— binding that associates aCompetencewith aCompetenceLevel, representing an employee's actual proficiency.Employee— person entity. Fields:id(non-empty string),username(non-empty string),competences(frozenset[EmployeeCompetence], defaults to empty). Each competence must appear at most once.RoleAssignment— links anEmployeeto aRole. Exposestraining_needs() -> TrainingNeedsand theidoneityproperty.TrainingNeed— records the training distance for a single competence:competence,relevance,proficiency_threshold,employee_level. Thepartial_distanceproperty computesmax(0, threshold - level) × relevance.TrainingNeeds— full gap analysis of an employee against a role:employee,role,needs(frozenset[TrainingNeed]). Thetotal_distanceproperty sums allpartial_distancevalues.idoneityproperty onRoleAssignment— computesΣ(level × relevance) / Σ(relevance)over all non-UNRELATEDrole competences. Absent competences default toNONE. Returns0.0when no relevant competences are defined.Scope.role_assignments—frozenset[RoleAssignment]field. Defaults to empty. Merges with child-override semantics keyed by(employee.id, role.name).Role,RoleCompetence,Competence,CompetenceLevel,CompetenceRelevance,Employee,EmployeeCompetence,RoleAssignment,TrainingNeed, andTrainingNeedsare now part of the public API, importable directly fromfushinryu_model.
[0.1.4] - 2026-06-09
Added
Commandment— mandatory rule value object with abodystring. Empty or whitespace-only body is rejected.Suggestion— recommended rule value object with identical structure. May be waived under justified conditions.Ruleset— groupscommandmentsandsuggestionsas two separatefrozensetfields (both default to empty).Category— recursive rule-category entity. Fields:name(identifier-pattern validated),applicability(mandatory string),tags(frozenset[str]),additive(bool, defaultFalse),ruleset(Ruleset),subcategories(frozenset[Category]). Subcategory names must be unique. Exposeseffective_commandmentsandeffective_suggestionsproperties that union recursively down the subcategory tree.Scope.rule_categories—frozenset[Category]field. Category names must be unique at the top level. Defaults to empty frozenset.- Rule-category merge semantics on
Scope.merge()andcollapse(): categories are matched bynameat each tree level and merged recursively. Commandments union by body (deduplicated). Suggestions replace the parent's unless the child category hasadditive=True, in which case they are unioned.collapse()propagates this transitively across the full ancestry DAG. Category,Commandment,Suggestion, andRulesetare now part of the public API, importable directly fromfushinryu_model.
[0.1.3] - 2026-06-06
Added
Task— new domain entity representing a concrete work item in a Scope's backlog. Fields:id,title,description,priority(TaskPriority),kind(TaskKind),lifecycle_status(TaskLifecycleStatus),on_hold,estimated_duration(timedelta | None),actual_duration(timedelta | None),related_acs(frozenset[str]).TaskPriorityenum —low,medium,high,critical.TaskKindenum —enhancement,bug,research,investigation,documentation,release,deployment.TaskLifecycleStatusenum —pending,doing,validation,done.Scope.backlog—frozenset[Task]field holding the scope's work items. Task ids must be unique within the backlog. Backlog merges across the scope hierarchy the same wayuser_storiesdoes.estimated_durationis mandatory for kindsenhancement,bug,documentation,release, anddeployment; optional forresearchandinvestigation.related_acsmerges as a union (same semantics asUserStory.tags).
[0.1.2] - 2026-06-06
Added
AcceptanceCriterion.requirement_specification— optional ISO 29148 document type (RequirementSpecification | None, defaults toNone). Accepted values:SRS,SyRS,StRS.AcceptanceCriterion.requirement_group— optional requirement category within the chosen specification (RequirementGroup | None, defaults toNone). Must be set together withrequirement_specificationand must be a group that belongs to that specification.RequirementSpecificationandRequirementGroupenums — now part of the public API, importable directly fromfushinryu_model.
[0.1.1] - 2026-06-06
Added
UserStory.dod— optional free-form prose field for the definition of done (str | None, defaults toNone).UserStory.tags— unordered set of short label strings (frozenset[str], defaults to empty). Tags merge as a union when parent and child stories are merged.AcceptanceCriterion.tags— same type and merge semantics asUserStory.tags.
Changed
Scope.descriptionis now optional (str | None, defaults toNone). Previously it was a required field.- Project renamed from
fushinryu_modeltofushinryu-modelto align with PyPI distribution naming conventions. The importable package name remainsfushinryu_model.
[0.1.0] - 2026-06-06
Initial release. Provides the core domain entities:
Scope— organisational unit with DAG parent hierarchy andcollapse().UserStory— structured requirement withwho / what / whyandUserStoryType.AcceptanceCriterion— Given / When / Then testable condition.ManualValidation/AutomatedValidation— immutable validation evidence.- Merge semantics for all entities via
pleroma.MergeableModel.