Class: WorkItem

Inherits:
Object
  • Object
show all
Defined in:
lib/almirah/doc_items/work_item.rb

Overview

A single Scope-table row of a Decision Record, modelled as a node in the per-row dependency network (ADR-194). Each work item carries its canonical identity (<record>.<step>.<activity>), the bounded per-row Status it reads its readiness from (ADR-193), and predecessor / successor edges spanning both intra-record (step order) and cross-record (Depends On) links.

predecessors / successors follow the house list-of-single-key-hashes shape (ADR-197): each entry maps a readable record.step.activity label to the resolved WorkItem. Every cross-record edge additionally records whether it stays within a decision_groups folder (in-group) or crosses to another (cross-group); that tag is inert here and consumed by the critical-chain step (ADR-195). Readiness ignores it — a cross-group predecessor blocks exactly like an in-group one.

Constant Summary collapse

ACTIVITY_ORDER =

Canonical phase order used only as the fallback for activity-type-aligned resolution when the prerequisite has no row of the dependent's exact Item.

%w[Analysis Requirements Code Tests].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(record_id:, step:, activity:, owner:, status:, depends_on_refs:, focused_estimate: 0.0, safe_estimate: 0.0, record_sequence: 0, start_date: nil, target_date: nil) ⇒ WorkItem

rubocop:disable Metrics/ParameterLists



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/almirah/doc_items/work_item.rb', line 25

def initialize(record_id:, step:, activity:, owner:, status:, depends_on_refs:, # rubocop:disable Metrics/ParameterLists
               focused_estimate: 0.0, safe_estimate: 0.0, record_sequence: 0,
               start_date: nil, target_date: nil)
  @record_id = record_id
  @step = step
  @activity = activity.to_s.strip
  @owner = owner.to_s.strip
  @status = status.to_s.strip
  @depends_on_refs = depends_on_refs # array of record ref strings, e.g. ["ADR-193"]
  # The row's authored committed window (ADR-213): the Scope Start Date and
  # Target Date as parsed Dates (nil when absent/unparseable). Distinct from the
  # computed schedule -- these are the author's intent, drawn on the tracking lane.
  @start_date = start_date
  @target_date = target_date
  # Per-row scheduling estimates in working days (ADR-195); the focused estimate
  # is the scheduling duration, safe - focused the safety aggregated into the
  # project buffer. record_sequence is the owning record's number, used only as
  # a deterministic priority tiebreak in the critical-chain scheduler.
  @focused_estimate = focused_estimate
  @safe_estimate = safe_estimate
  @record_sequence = record_sequence
  @predecessors = []
  @successors = []
  @cross_group_predecessor_labels = []
  # One entry per resolved Depends On reference, keyed by the authored ref:
  # { ref => { doc:, anchor:, label: } }. `anchor` is the target's resolved
  # work-item row anchor (nil when the target has no `#` step column, so the
  # link opens the record page); `label` is the resolved work item's id, for
  # the link tooltip. Used to render the Depends On cell as deep links.
  @resolved_dependencies = {}
end

Instance Attribute Details

#activityObject (readonly)

Returns the value of attribute activity.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def activity
  @activity
end

#cross_group_predecessor_labelsObject

Returns the value of attribute cross_group_predecessor_labels.



23
24
25
# File 'lib/almirah/doc_items/work_item.rb', line 23

def cross_group_predecessor_labels
  @cross_group_predecessor_labels
end

#depends_on_refsObject (readonly)

Returns the value of attribute depends_on_refs.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def depends_on_refs
  @depends_on_refs
end

#focused_estimateObject (readonly)

Returns the value of attribute focused_estimate.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def focused_estimate
  @focused_estimate
end

#ownerObject (readonly)

Returns the value of attribute owner.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def owner
  @owner
end

#predecessorsObject

Returns the value of attribute predecessors.



23
24
25
# File 'lib/almirah/doc_items/work_item.rb', line 23

def predecessors
  @predecessors
end

#record_idObject (readonly)

Returns the value of attribute record_id.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def record_id
  @record_id
end

#record_sequenceObject (readonly)

Returns the value of attribute record_sequence.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def record_sequence
  @record_sequence
end

#resolved_dependenciesObject

Returns the value of attribute resolved_dependencies.



23
24
25
# File 'lib/almirah/doc_items/work_item.rb', line 23

def resolved_dependencies
  @resolved_dependencies
end

#safe_estimateObject (readonly)

Returns the value of attribute safe_estimate.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def safe_estimate
  @safe_estimate
end

#start_dateObject (readonly)

Returns the value of attribute start_date.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def start_date
  @start_date
end

#statusObject (readonly)

Returns the value of attribute status.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def status
  @status
end

#stepObject (readonly)

Returns the value of attribute step.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def step
  @step
end

#successorsObject

Returns the value of attribute successors.



23
24
25
# File 'lib/almirah/doc_items/work_item.rb', line 23

def successors
  @successors
end

#target_dateObject (readonly)

Returns the value of attribute target_date.



21
22
23
# File 'lib/almirah/doc_items/work_item.rb', line 21

def target_date
  @target_date
end

Instance Method Details

#activity_rankObject



73
74
75
# File 'lib/almirah/doc_items/work_item.rb', line 73

def activity_rank
  ACTIVITY_ORDER.index(@activity) || ACTIVITY_ORDER.length
end

#add_predecessor(work_item, cross_group:) ⇒ Object



87
88
89
90
# File 'lib/almirah/doc_items/work_item.rb', line 87

def add_predecessor(work_item, cross_group:)
  @predecessors << { work_item.id => work_item }
  @cross_group_predecessor_labels << work_item.id if cross_group
end

#add_resolved_dependency(ref, doc, anchor, label) ⇒ Object



69
70
71
# File 'lib/almirah/doc_items/work_item.rb', line 69

def add_resolved_dependency(ref, doc, anchor, label)
  @resolved_dependencies[ref] = { doc: doc, anchor: anchor, label: label }
end

#add_successor(work_item) ⇒ Object



92
93
94
# File 'lib/almirah/doc_items/work_item.rb', line 92

def add_successor(work_item)
  @successors << { work_item.id => work_item }
end

#cross_record_predecessorsObject

Resolved Depends On edges into other records (in-group or cross-group alike).



110
111
112
# File 'lib/almirah/doc_items/work_item.rb', line 110

def cross_record_predecessors
  predecessor_items.reject { |p| p.record_id == @record_id }
end

#cross_record_violation?Boolean

A started row whose resolved cross-record predecessor is not yet Done.

Returns:

  • (Boolean)


126
127
128
# File 'lib/almirah/doc_items/work_item.rb', line 126

def cross_record_violation?
  started? && cross_record_predecessors.any? { |p| !p.done? }
end

#done?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/almirah/doc_items/work_item.rb', line 83

def done?
  @status == 'Done'
end

#fully_kitted?Boolean

Kitted when every predecessor — intra- and cross-record, regardless of the in-group / cross-group tag — is Done; trivially kitted when it has none.

Returns:

  • (Boolean)


116
117
118
# File 'lib/almirah/doc_items/work_item.rb', line 116

def fully_kitted?
  predecessor_items.all?(&:done?)
end

#idObject



57
58
59
# File 'lib/almirah/doc_items/work_item.rb', line 57

def id
  "#{@record_id}.#{@step}.#{@activity}"
end

#intra_record_predecessorsObject

Same-record, lower-numbered steps — the intra-record (phase-order) edges.



105
106
107
# File 'lib/almirah/doc_items/work_item.rb', line 105

def intra_record_predecessors
  predecessor_items.select { |p| p.record_id == @record_id }
end

#phase_order_violation?Boolean

A started row with an unfinished lower-numbered step in the same record.

Returns:

  • (Boolean)


121
122
123
# File 'lib/almirah/doc_items/work_item.rb', line 121

def phase_order_violation?
  started? && intra_record_predecessors.any? { |p| !p.done? }
end

#predecessor_itemsObject



96
97
98
# File 'lib/almirah/doc_items/work_item.rb', line 96

def predecessor_items
  @predecessors.map { |edge| edge.values.first }
end

#row_anchorObject

The anchor of this work item's row in its rendered Scope table — the deep-link target a dependent record points at. Namespaced with .scope. so it never collides with the Affected Documents controlled table, whose rows anchor on the same <record>.<step> scheme on the same page.



65
66
67
# File 'lib/almirah/doc_items/work_item.rb', line 65

def row_anchor
  "#{@record_id}.scope.#{@step}"
end

#started?Boolean

A row is "started" once it leaves To Do; both In-Progress and Done count, so a Done row whose predecessor never finished is still flagged as a violation.

Returns:

  • (Boolean)


79
80
81
# File 'lib/almirah/doc_items/work_item.rb', line 79

def started?
  @status == 'In-Progress' || @status == 'Done'
end

#successor_itemsObject



100
101
102
# File 'lib/almirah/doc_items/work_item.rb', line 100

def successor_items
  @successors.map { |edge| edge.values.first }
end