scriptplan 0.9.2__tar.gz → 0.10.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. scriptplan-0.10.1/CHANGELOG.md +209 -0
  2. {scriptplan-0.9.2 → scriptplan-0.10.1}/MANIFEST.in +1 -0
  3. {scriptplan-0.9.2/scriptplan.egg-info → scriptplan-0.10.1}/PKG-INFO +68 -39
  4. {scriptplan-0.9.2 → scriptplan-0.10.1}/README.md +66 -37
  5. {scriptplan-0.9.2 → scriptplan-0.10.1}/pyproject.toml +11 -5
  6. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/__init__.py +3 -1
  7. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/cli/plan.py +42 -321
  8. scriptplan-0.10.1/scriptplan/cli/plan_files.py +195 -0
  9. scriptplan-0.10.1/scriptplan/cli/plan_help.py +194 -0
  10. scriptplan-0.10.1/scriptplan/core/allocation_plan.py +171 -0
  11. scriptplan-0.10.1/scriptplan/core/attributes.py +464 -0
  12. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/exceptions.py +319 -319
  13. scriptplan-0.10.1/scriptplan/core/limits.py +335 -0
  14. scriptplan-0.10.1/scriptplan/core/project.py +519 -0
  15. scriptplan-0.10.1/scriptplan/core/project_attributes.py +212 -0
  16. scriptplan-0.10.1/scriptplan/core/project_calendar.py +126 -0
  17. scriptplan-0.10.1/scriptplan/core/project_tree.py +256 -0
  18. scriptplan-0.10.1/scriptplan/core/property.py +100 -0
  19. scriptplan-0.10.1/scriptplan/core/property_list.py +358 -0
  20. scriptplan-0.10.1/scriptplan/core/property_set.py +338 -0
  21. scriptplan-0.10.1/scriptplan/core/property_tree_node.py +334 -0
  22. scriptplan-0.10.1/scriptplan/core/resolution.py +95 -0
  23. scriptplan-0.10.1/scriptplan/core/resource_availability.py +187 -0
  24. scriptplan-0.10.1/scriptplan/core/resource_reporting.py +311 -0
  25. scriptplan-0.10.1/scriptplan/core/resource_scenario.py +360 -0
  26. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/scenario_data.py +1 -2
  27. scriptplan-0.10.1/scriptplan/core/slot_ledger.py +234 -0
  28. scriptplan-0.10.1/scriptplan/core/task_booking.py +276 -0
  29. scriptplan-0.10.1/scriptplan/core/task_dependencies.py +267 -0
  30. scriptplan-0.10.1/scriptplan/core/task_placement.py +431 -0
  31. scriptplan-0.10.1/scriptplan/core/task_resources.py +335 -0
  32. scriptplan-0.10.1/scriptplan/core/task_scenario.py +434 -0
  33. scriptplan-0.10.1/scriptplan/core/timezones.py +59 -0
  34. scriptplan-0.10.1/scriptplan/core/units.py +190 -0
  35. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/working_hours.py +78 -70
  36. scriptplan-0.10.1/scriptplan/parser/attribute_apply.py +302 -0
  37. scriptplan-0.10.1/scriptplan/parser/model_pieces.py +227 -0
  38. scriptplan-0.10.1/scriptplan/parser/report_builder.py +223 -0
  39. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/parser/tjp.lark +29 -6
  40. scriptplan-0.10.1/scriptplan/parser/tjp_parser.py +416 -0
  41. scriptplan-0.10.1/scriptplan/parser/transformer_project.py +193 -0
  42. scriptplan-0.10.1/scriptplan/parser/transformer_report.py +285 -0
  43. scriptplan-0.10.1/scriptplan/parser/transformer_resource.py +224 -0
  44. scriptplan-0.10.1/scriptplan/parser/transformer_task.py +339 -0
  45. scriptplan-0.10.1/scriptplan/report/logical_expression.py +392 -0
  46. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/report/report.py +51 -15
  47. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/report/report_base.py +27 -6
  48. scriptplan-0.10.1/scriptplan/report/table_cells.py +165 -0
  49. scriptplan-0.10.1/scriptplan/report/table_headers.py +69 -0
  50. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/report/table_report.py +32 -160
  51. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/report/task_report.py +4 -7
  52. scriptplan-0.10.1/scriptplan/scheduler/postcheck.py +106 -0
  53. scriptplan-0.10.1/scriptplan/scheduler/preflight.py +121 -0
  54. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/scheduler/scoreboard.py +1 -47
  55. scriptplan-0.10.1/scriptplan/utils/data_cache.py +9 -0
  56. scriptplan-0.10.1/scriptplan/utils/logger.py +39 -0
  57. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/utils/message_handler.py +103 -231
  58. scriptplan-0.10.1/scriptplan/utils/messages.py +192 -0
  59. {scriptplan-0.9.2 → scriptplan-0.10.1/scriptplan.egg-info}/PKG-INFO +68 -39
  60. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan.egg-info/SOURCES.txt +40 -10
  61. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan.egg-info/requires.txt +0 -1
  62. scriptplan-0.10.1/setup.py +17 -0
  63. scriptplan-0.10.1/tests/test_ansi_color.py +24 -0
  64. scriptplan-0.10.1/tests/test_audit_probes.py +78 -0
  65. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_core_resource.py +1 -1
  66. scriptplan-0.10.1/tests/test_fixture_golden.py +63 -0
  67. scriptplan-0.10.1/tests/test_plan_cli.py +139 -0
  68. scriptplan-0.10.1/tests/test_project_isolation.py +89 -0
  69. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_property_set.py +4 -2
  70. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_report.py +2 -2
  71. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_scheduling.py +3 -3
  72. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_scheduling_validation.py +129 -210
  73. scriptplan-0.10.1/tests/test_slot_ledger.py +118 -0
  74. scriptplan-0.9.2/scriptplan/_cython/__init__.py +0 -45
  75. scriptplan-0.9.2/scriptplan/_cython/scoreboard_cy.c +0 -11547
  76. scriptplan-0.9.2/scriptplan/_cython/scoreboard_cy.pyx +0 -174
  77. scriptplan-0.9.2/scriptplan/_cython/time_utils_cy.c +0 -11618
  78. scriptplan-0.9.2/scriptplan/_cython/time_utils_cy.pyx +0 -149
  79. scriptplan-0.9.2/scriptplan/_cython/working_hours_cy.c +0 -11742
  80. scriptplan-0.9.2/scriptplan/_cython/working_hours_cy.pyx +0 -150
  81. scriptplan-0.9.2/scriptplan/core/limits.py +0 -369
  82. scriptplan-0.9.2/scriptplan/core/project.py +0 -948
  83. scriptplan-0.9.2/scriptplan/core/property.py +0 -1323
  84. scriptplan-0.9.2/scriptplan/core/resource_scenario.py +0 -719
  85. scriptplan-0.9.2/scriptplan/core/task_scenario.py +0 -1590
  86. scriptplan-0.9.2/scriptplan/parser/tjp_parser.py +0 -1845
  87. scriptplan-0.9.2/scriptplan/scheduler/batch_processor.py +0 -233
  88. scriptplan-0.9.2/scriptplan/utils/data_cache.py +0 -50
  89. scriptplan-0.9.2/scriptplan/utils/logger.py +0 -246
  90. scriptplan-0.9.2/setup.py +0 -66
  91. scriptplan-0.9.2/tests/test_batch_processor.py +0 -118
  92. scriptplan-0.9.2/tests/test_logger.py +0 -148
  93. {scriptplan-0.9.2 → scriptplan-0.10.1}/LICENSE +0 -0
  94. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/cli/__init__.py +0 -0
  95. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/cli/main.py +0 -0
  96. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/__init__.py +0 -0
  97. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/account.py +0 -0
  98. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/allocation.py +0 -0
  99. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/booking.py +0 -0
  100. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/journal.py +0 -0
  101. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/leave.py +0 -0
  102. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/resource.py +0 -0
  103. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/scenario.py +0 -0
  104. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/shift.py +0 -0
  105. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/task.py +0 -0
  106. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/core/timesheet.py +0 -0
  107. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/parser/__init__.py +0 -0
  108. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/parser/macro_processor.py +0 -0
  109. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/py.typed +0 -0
  110. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/report/__init__.py +0 -0
  111. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/report/report_context.py +0 -0
  112. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/report/resource_report.py +0 -0
  113. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/report/text_report.py +0 -0
  114. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/scheduler/__init__.py +0 -0
  115. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/utils/__init__.py +0 -0
  116. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan/utils/time.py +0 -0
  117. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan.egg-info/dependency_links.txt +0 -0
  118. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan.egg-info/entry_points.txt +0 -0
  119. {scriptplan-0.9.2 → scriptplan-0.10.1}/scriptplan.egg-info/top_level.txt +0 -0
  120. {scriptplan-0.9.2 → scriptplan-0.10.1}/setup.cfg +0 -0
  121. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_account.py +0 -0
  122. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_attribute_base.py +0 -0
  123. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_cli.py +0 -0
  124. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_core_allocation.py +0 -0
  125. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_core_booking.py +0 -0
  126. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_core_project.py +0 -0
  127. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_core_scenario.py +0 -0
  128. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_core_task.py +0 -0
  129. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_journal.py +0 -0
  130. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_macro_processor.py +0 -0
  131. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_message_handler.py +0 -0
  132. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_parser.py +0 -0
  133. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_property_list.py +0 -0
  134. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_property_tree_node.py +0 -0
  135. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_scoreboard.py +0 -0
  136. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_shift.py +0 -0
  137. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_timesheet.py +0 -0
  138. {scriptplan-0.9.2 → scriptplan-0.10.1}/tests/test_utils_time.py +0 -0
@@ -0,0 +1,209 @@
1
+ # Changelog
2
+
3
+ ## 0.10.1
4
+
5
+ Tasks that shared a timing slot on one resource could be booked into time that
6
+ does not exist. Three tasks of 40, 13 and 40 minutes on one person at
7
+ `timingresolution 60min` all started at 09:00, and the 09:00 slot held 5580
8
+ seconds of work without any warning. Every release from 0.9.0 to 0.10.0 has
9
+ the defect. Trace's scheduling harness found it.
10
+
11
+ When a task finished part-way through a slot, the engine gave the rest of that
12
+ slot back as if the task had booked all of it, even when an earlier task had
13
+ already used the front of the slot. It also placed every later task at the top
14
+ of the slot. Each booking now records where in the slot it sits. Forward work
15
+ fills a slot from its start and backward (`alap`) work from its end, and a task
16
+ that finishes early hands back only the part of its own booking it did not use.
17
+ The same three tasks now run 09:00-09:40, 09:40-09:53 and 09:53-10:33 at any
18
+ resolution.
19
+
20
+ ### Fixed
21
+
22
+ - A task whose first slot was partly used reported its start at the top of that
23
+ slot. At 5-minute resolution the third task above started at 09:50 while the
24
+ second ran until 09:53. It now starts at 09:53.
25
+ - `alap` tasks sharing a slot overbooked it the same way. They now fill it
26
+ backwards from the end without overlapping.
27
+ - When several resources finished a task in the same slot, only one of them
28
+ handed back its unused time. 80 minutes of effort on two people ended at
29
+ 10:00 and cost two hours. It now ends at 09:40 and costs 80 minutes.
30
+ - Time handed back was not returned to `limits`. Under `dailymax 60min` the
31
+ three tasks above took Monday, Tuesday and Wednesday, and the person worked
32
+ 40 minutes on Monday. They now use exactly 60 minutes on Monday and finish on
33
+ Tuesday at 09:33.
34
+ - A task that could start mid-slot because its predecessor ended there was kept
35
+ out of the first part of every slot until it booked something. When the
36
+ resource was busy in that slot, the task lost the same minutes in the next
37
+ one, and a task free to start at 10:00 started at 10:50.
38
+ - A `start` inside a slot was rounded down. `start 2026-10-19-09:30` at 60-minute
39
+ resolution started at 09:00, half an hour before the date in the file. A
40
+ mid-slot `end` on an `alap` task was also rounded down to the start of its
41
+ slot. Both are now honoured to the minute.
42
+ - A milestone after a task that ended mid-slot sat at the top of that slot,
43
+ before its predecessor had finished. It now sits at the moment the
44
+ predecessor ends.
45
+
46
+ ### Schedules that move
47
+
48
+ A plan in which no work starts or ends inside a slot schedules as it did in
49
+ 0.10.0. Results change where efforts are off the slot grid, where an efficiency
50
+ other than 1 makes work end mid-slot, where several resources share a task, and
51
+ where a date falls inside a slot. Of the 24 example projects, 22 schedule
52
+ identically to 0.10.0:
53
+
54
+ - `airport_stress_test.tjp`: `t_install` starts at 11:40 instead of 11:00,
55
+ which is when the electrician finishes `t_crit`. `t_low` starts at 10:20
56
+ instead of 10:00 for the same reason. Both keep the end times they had.
57
+ - `tutorial.tjp`: `test.alpha` ends at 11:36 instead of 12:00 on 3 April because
58
+ both people on it stop together. `test.beta` and the project finish at 12:48
59
+ instead of 13:00, and the `beta` and `done` milestones follow.
60
+
61
+ ### Not fixed in this release
62
+
63
+ A `duration` or `length` task that depends on a task ending mid-slot still
64
+ starts at the top of that slot, before its predecessor has finished. After a
65
+ task ending at 09:40, `duration 2h` is reported as 09:00-11:00. 0.10.0 does the
66
+ same. `TaskScenario.getAllocatedTime()` still credits a shared slot to the last
67
+ task booked in it, although cost is correct.
68
+
69
+ ### Internal API
70
+
71
+ `ResourceScenario.book()` takes a `forward` argument. `reserve()` and
72
+ `release()` are new and return or take a `SlotBooking` from
73
+ `scriptplan.core.slot_ledger`. `markSlotPartiallyUsed()` and
74
+ `releasePartialSlot()` had no callers and are removed. `slotSecondsUsed` also
75
+ counts the time a task skips to honour a mid-slot start or end. To measure the work
76
+ booked in a slot, sum `slotTaskUsage`.
77
+
78
+ ### Verification
79
+
80
+ The probe named "Two tasks sharing one resource never double-book a slot" used
81
+ efforts of whole hours, which never share a slot, and it checked the total
82
+ booked, which stays correct when work overlaps. Every capacity check in the
83
+ harness read `slotSecondsUsed`, and the defect lowered that counter in the same
84
+ step that overbooked the slot. None of them could fail on this defect.
85
+
86
+ `audit/evaluations/probe_slot_sharing.py` checks each slot against what was
87
+ booked into it, compares the CLI's report with dates worked out by hand, and
88
+ runs 360 seeded random plans, 240 of them with efforts anywhere from 1 to 600
89
+ minutes. Against 0.10.0 all nine of its probes fail, including the 490-minute
90
+ day Trace reported, and 115 of those 240 plans break at least one of its
91
+ checks. Against 0.10.1 all nine pass. The harness now sums `slotTaskUsage`
92
+ wherever it measures booked time.
93
+
94
+ ## 0.10.0
95
+
96
+ A readiness audit of 0.9.2 turned every documented claim into a probe that
97
+ drives ScriptPlan through its own API or CLI. 32 of those probes failed. This
98
+ release is what it took to make them pass, and it changes scheduling results.
99
+
100
+ ### Breaking: `effort` is person-time
101
+
102
+ `effort 16h allocate a, b` used to occupy both resources for sixteen hours and
103
+ credit sixteen hours of work. Two resources booked thirty-two hours, four booked
104
+ sixty-four, and the completion date never moved however many were assigned. The
105
+ engine took the largest contribution in each slot rather than the sum, so the
106
+ labour of every resource but one was booked, billed and discarded.
107
+
108
+ Effort is now the amount of work to be done, and each allocated resource
109
+ contributes its own time towards it, as TaskJuggler defines it. The same task
110
+ with two resources finishes in half the time and costs what one resource's worth
111
+ of work costs.
112
+
113
+ **Upgrading.** A task whose allocations were meant to work side by side is
114
+ written with `mandatory` and its effort in person-hours:
115
+
116
+ ```
117
+ # before 0.10.0: four hours of a pair working together
118
+ task deploy "Deploy" { effort 4h allocate dev, qa }
119
+
120
+ # 0.10.0 and after
121
+ task deploy "Deploy" { effort 8h allocate dev { mandatory }, qa { mandatory } }
122
+ ```
123
+
124
+ Tasks with a single allocation are unaffected. Of the 24 example projects, 21
125
+ schedule identically to 0.9.2; the three that differ are those that allocate
126
+ more than one resource to a task.
127
+
128
+ ### Breaking: failures are reported as failures
129
+
130
+ `Project.schedule()` always returned `True`, and the CLI exited 0 with empty
131
+ start and end cells. A circular dependency, an effort task with no allocation,
132
+ or a task that did not fit the project window produced a warning nobody saw.
133
+
134
+ Scheduling now returns the real result and names each failing task and why; the
135
+ CLIs exit non-zero. A dependency cycle is rejected before scheduling, naming the
136
+ cycle. An attribute or reference the parser cannot place is an error rather than
137
+ a silent drop, which is how `duration`, `length`, `complete` and `note` reached
138
+ the model for the first time.
139
+
140
+ ### Breaking: the declared project period is honoured
141
+
142
+ The project end was grown by a heuristic before scheduling. It is now taken as
143
+ written, and a task that does not fit is reported. Projects that relied on the
144
+ extension need their period stated honestly.
145
+
146
+ ### Breaking: unimplemented report formats are rejected
147
+
148
+ `ical`, `tjp`, `niku`, `ctags` and MS Project XML were accepted, wrote no file,
149
+ and left the CLI blaming a scheduling problem that did not exist. They now raise
150
+ at parse time. Use `json` or `csv`.
151
+
152
+ ### Fixed
153
+
154
+ - Durations follow one table: `m` is months and `min` is minutes everywhere;
155
+ `duration` and `gapduration` are calendar time, `effort`, `length` and
156
+ `gaplength` working time derived from `dailyworkinghours` and
157
+ `yearlyworkingdays`. `gapduration 2d` was 16 hours; `gaplength 30min` was no
158
+ gap at all; `limits { dailymax 30min }` was a 4800-hour cap.
159
+ - Limits count seconds in the resource's own calendar and admit part of a slot,
160
+ so `dailymax 6.4h` is exact. A resource in Los Angeles capped at four hours a
161
+ day was working five in one of its own days. `dailymin`, `weeklymin`,
162
+ `monthlymax`, `monthlymin`, `maximum` and `minimum` reach the grammar, as do
163
+ `maxend`, `maxstart`, `minend` and `minstart`, and violations are reported.
164
+ - Dates are read in the project's timezone and scheduled in UTC; reports convert
165
+ back. The default working day was compared against the hour of a UTC slot,
166
+ putting a New York project five hours out.
167
+ - Slot size is derived from the times a project uses, so `start 09:30` and a
168
+ shift of `08:13 - 11:59` are exact without setting `timingresolution`. That
169
+ shift was credited with seven hours a day instead of eight and a half.
170
+ - A working-hours interval crossing midnight applies to the following day.
171
+ `fri 22:00 - 06:00` marked Friday morning instead of Saturday.
172
+ - Chains mixing ASAP and ALAP schedule instead of deadlocking.
173
+ - Scheduling twice gives the same dates and the same bookings. The second run
174
+ kept the dates and discarded every booking.
175
+ - Reports honour their own `sorttasks`, `hidetask`, column options and formats,
176
+ and `plan report` renders the reports a project declares rather than
177
+ substituting a three-column one. Descending sort was a no-op, and any filter
178
+ hid every row.
179
+ - `report_id` is a SHA-256 of the rendered result. It hashed the input file, so
180
+ a correct and a broken schedule of the same input shared an id.
181
+ - `mandatory`, `persistent` and per-resource allocation options work; they
182
+ parsed to nothing.
183
+
184
+ ### Removed
185
+
186
+ - The Cython extensions. Under `cdivision` the compiled path computed
187
+ `(0 - 1) % 7` as `-1`, so a resource with a Sunday night shift lost its Monday
188
+ morning in every published wheel and kept it in every source checkout: the
189
+ same file, two schedules, and no test covering the compiled one. The wheel is
190
+ now pure Python and cannot diverge from its source.
191
+ - `scheduler.batch_processor`, `utils.data_cache.DataCache` and `utils.Log`,
192
+ none of which were reachable.
193
+
194
+ ### Performance
195
+
196
+ 500 chained tasks at minute resolution schedule in 3.3s, from 14.4s. A year at
197
+ minute resolution with ten resources takes about 13s, from 58.7s.
198
+
199
+ ### Verification
200
+
201
+ `audit/evaluations/` holds 74 probes across 12 files, each named after the
202
+ behaviour it would falsify and each driving ScriptPlan through its own
203
+ interface. `scripts/ci.sh` runs lint, types, a file-size cap, 411 tests, a
204
+ showcase currency check and the probes. The probes also pass against the built
205
+ wheel installed in a clean virtualenv, so what is verified is what ships.
206
+
207
+ ## 0.9.2 and earlier
208
+
209
+ See the git history.
@@ -1,5 +1,6 @@
1
1
  include LICENSE
2
2
  include README.md
3
+ include CHANGELOG.md
3
4
  include pyproject.toml
4
5
  recursive-include scriptplan *.lark
5
6
  recursive-include scriptplan py.typed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scriptplan
3
- Version: 0.9.2
3
+ Version: 0.10.1
4
4
  Summary: A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management.
5
5
  Author-email: Farshid Ashouri <farsheed.ashouri@gmail.com>
6
6
  Maintainer-email: Farshid Ashouri <farsheed.ashouri@gmail.com>
@@ -9,6 +9,7 @@ Project-URL: Homepage, https://github.com/rodmena-limited/scriptplan
9
9
  Project-URL: Documentation, https://github.com/rodmena-limited/scriptplan#readme
10
10
  Project-URL: Bug Tracker, https://github.com/rodmena-limited/scriptplan/issues
11
11
  Project-URL: Source, https://github.com/rodmena-limited/scriptplan
12
+ Project-URL: Changelog, https://github.com/rodmena-limited/scriptplan/blob/main/CHANGELOG.md
12
13
  Keywords: scheduling,project-management,resource-allocation,gantt,critical-path
13
14
  Classifier: Development Status :: 4 - Beta
14
15
  Classifier: Intended Audience :: Developers
@@ -40,7 +41,6 @@ Requires-Dist: build; extra == "dev"
40
41
  Requires-Dist: twine; extra == "dev"
41
42
  Requires-Dist: ruff>=0.1.0; extra == "dev"
42
43
  Requires-Dist: mypy>=1.0.0; extra == "dev"
43
- Requires-Dist: cython>=3.0.0; extra == "dev"
44
44
  Dynamic: license-file
45
45
 
46
46
  <p align="center">
@@ -49,11 +49,10 @@ Dynamic: license-file
49
49
 
50
50
  # ScriptPlan
51
51
 
52
- ![Logic Certified](https://img.shields.io/badge/Logic-Certified_Airport--Grade-success?style=for-the-badge&logo=github)
53
- ![Tests Passed](https://img.shields.io/badge/Stress_Tests-24%2F24_Passed-success?style=for-the-badge)
54
- ![Temporal Physics](https://img.shields.io/badge/Temporal_Physics-Verified-blue?style=for-the-badge)
55
- ![Scheduling](https://img.shields.io/badge/Scheduling-ALAP_%7C_JIT_%7C_Priority-blueviolet?style=for-the-badge)
56
- ![Constraints](https://img.shields.io/badge/Constraints-Hard_Limits_%7C_Quotas-orange?style=for-the-badge)
52
+ ![Tests](https://img.shields.io/badge/tests-411_passing-success?style=for-the-badge)
53
+ ![Probes](https://img.shields.io/badge/audit_probes-67_passing-success?style=for-the-badge)
54
+ ![Python](https://img.shields.io/badge/python-3.9%2B-blue?style=for-the-badge)
55
+ ![Licence](https://img.shields.io/badge/licence-Apache--2.0-lightgrey?style=for-the-badge)
57
56
 
58
57
  A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management. The syntax is compatible with TaskJuggler (.tjp files).
59
58
 
@@ -110,11 +109,11 @@ done
110
109
  **Features**:
111
110
  - Output to stdout (Unix philosophy)
112
111
  - Messages to stderr
113
- - SHA256 report_id (content-based hash)
112
+ - SHA256 `report_id` over the rendered result, so it changes when the schedule does
114
113
  - Lowercase column names
115
114
  - No HTML metadata
116
115
  - No file pollution (uses temp directories)
117
- - Safe for concurrent execution (100+ instances)
116
+ - Separate processes do not collide: temporary files and directories are unique per run
118
117
 
119
118
  ### Python API
120
119
 
@@ -132,39 +131,69 @@ for task in project.tasks:
132
131
  print(f"{task.id}: {start} -> {end}")
133
132
  ```
134
133
 
135
- ## System Certified
134
+ ## What is verified
136
135
 
137
- **Certification Level**: Airport-Grade / Mission Critical
136
+ Each behaviour below has a probe in `audit/evaluations/` that drives ScriptPlan
137
+ through its own API or CLI and asserts the result. They run as part of the test
138
+ suite, and can be run on their own against an installed release:
138
139
 
139
- **Capabilities Verified**:
140
-
141
- - **Temporal Physics**: Floating point precision, Timezones, DST awareness, Date Line crossing.
142
-
143
- - **Resource Constraints**: Daily/Weekly limits, Hierarchical quotas, Shift intersections.
144
-
145
- - **Advanced Scheduling**: ALAP (Backward pass), Priority preemption, Smart Resource Selection (Failover).
146
-
147
- - **Workflow Logic**: Atomicity (Contiguous), Perishability (Max Gap), Zero-Buffer Synchronization.
148
-
149
- ## Features
150
-
151
- - Minute-level scheduling precision
152
- - ASAP and ALAP scheduling modes
153
- - Resource allocation with contention handling
154
- - Working hours and shift definitions
155
- - Dependency chains with gap constraints
156
- - Leap year and timezone handling
157
-
158
- ## Accuracy
159
-
160
- ScriptPlan uses integer arithmetic for all time calculations, avoiding floating-point drift. The scheduler correctly handles:
161
-
162
- - Non-standard shift boundaries (e.g., 08:13 - 11:59, 13:07 - 17:47)
163
- - Prime-number effort durations with prime-number gaps
164
- - Resource contention across multi-day tasks
165
- - Calendar gaps (weekends, holidays) vs working time gaps
140
+ ```bash
141
+ audit/evaluations/run_all.sh
142
+ ```
166
143
 
167
- Example: A chain of 500 tasks, each with 73-minute effort and 29-minute gaps, scheduled across leap year boundaries with non-standard shifts, produces exact minute-aligned results.
144
+ **Time**
145
+
146
+ - Dates are read in the project's timezone and scheduled in UTC; reports convert back.
147
+ - Resource shifts follow their own timezone across daylight-saving changes in both directions.
148
+ - A working-hours interval that crosses midnight applies to the following day.
149
+ - Slot size is derived from the times a project uses, so `start 09:30` is honoured
150
+ exactly and a shift of `08:13 - 11:59, 13:07 - 17:47` yields its true 8h26m a day.
151
+ `timingresolution` overrides this.
152
+
153
+ **Units**
154
+
155
+ - Calendar time (`duration`, `gapduration`) and working time (`effort`, `length`,
156
+ `gaplength`) are distinguished, with working time derived from
157
+ `dailyworkinghours` and `yearlyworkingdays`. `m` means months; `min` means minutes.
158
+
159
+ **Resources**
160
+
161
+ - `effort` is person-time: two resources available in a slot contribute two
162
+ slots of it. `mandatory` requires a resource for the task to progress at all;
163
+ `persistent` keeps the one first chosen; `alternative` falls back.
164
+ - `dailymax`, `weeklymax`, `monthlymax`, `maximum` and their `min` counterparts
165
+ block at the cap **and** release in the next period. Periods are counted in the
166
+ resource's own calendar, and a cap smaller than one slot is honoured exactly.
167
+
168
+ **Scheduling**
169
+
170
+ - ASAP and ALAP, including chains that mix the two.
171
+ - Contention is resolved by priority, then criticalness, then declaration order.
172
+ - The declared project period is never rewritten. A task that does not fit is
173
+ reported, with its name and the reason.
174
+ - Scheduling the same project twice produces the same dates and the same bookings.
175
+ - `maxgapduration`, `maxgaplength`, `maxstart`, `minstart`, `maxend` and `minend`
176
+ are checked once the schedule is complete, and a violation is an error.
177
+
178
+ **Honesty**
179
+
180
+ - A project that cannot be scheduled exits non-zero and says why. A dependency
181
+ cycle is rejected before scheduling, naming the cycle.
182
+ - An attribute or reference the parser cannot place is an error, never a silent
183
+ drop.
184
+ - `report_id` is a SHA-256 of the rendered result, so it differs when the
185
+ schedule differs.
186
+
187
+ ## Known limits
188
+
189
+ - `include`, `supplement`, `leaveallowance` and `statussheet` are not implemented.
190
+ - Output formats are JSON and CSV. `ical`, `tjp`, `niku` and MS Project XML are
191
+ rejected at parse time rather than silently producing nothing.
192
+ - Accounts are parsed but not yet modelled, so `chargeset` and `charge` record an
193
+ amount without posting it anywhere.
194
+ - `onstart` dependencies are read as "the dependent task must finish before the
195
+ predecessor starts", which is ScriptPlan's own reading rather than
196
+ TaskJuggler's start-to-start one.
168
197
 
169
198
  ## Example Project
170
199
 
@@ -4,11 +4,10 @@
4
4
 
5
5
  # ScriptPlan
6
6
 
7
- ![Logic Certified](https://img.shields.io/badge/Logic-Certified_Airport--Grade-success?style=for-the-badge&logo=github)
8
- ![Tests Passed](https://img.shields.io/badge/Stress_Tests-24%2F24_Passed-success?style=for-the-badge)
9
- ![Temporal Physics](https://img.shields.io/badge/Temporal_Physics-Verified-blue?style=for-the-badge)
10
- ![Scheduling](https://img.shields.io/badge/Scheduling-ALAP_%7C_JIT_%7C_Priority-blueviolet?style=for-the-badge)
11
- ![Constraints](https://img.shields.io/badge/Constraints-Hard_Limits_%7C_Quotas-orange?style=for-the-badge)
7
+ ![Tests](https://img.shields.io/badge/tests-411_passing-success?style=for-the-badge)
8
+ ![Probes](https://img.shields.io/badge/audit_probes-67_passing-success?style=for-the-badge)
9
+ ![Python](https://img.shields.io/badge/python-3.9%2B-blue?style=for-the-badge)
10
+ ![Licence](https://img.shields.io/badge/licence-Apache--2.0-lightgrey?style=for-the-badge)
12
11
 
13
12
  A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management. The syntax is compatible with TaskJuggler (.tjp files).
14
13
 
@@ -65,11 +64,11 @@ done
65
64
  **Features**:
66
65
  - Output to stdout (Unix philosophy)
67
66
  - Messages to stderr
68
- - SHA256 report_id (content-based hash)
67
+ - SHA256 `report_id` over the rendered result, so it changes when the schedule does
69
68
  - Lowercase column names
70
69
  - No HTML metadata
71
70
  - No file pollution (uses temp directories)
72
- - Safe for concurrent execution (100+ instances)
71
+ - Separate processes do not collide: temporary files and directories are unique per run
73
72
 
74
73
  ### Python API
75
74
 
@@ -87,39 +86,69 @@ for task in project.tasks:
87
86
  print(f"{task.id}: {start} -> {end}")
88
87
  ```
89
88
 
90
- ## System Certified
89
+ ## What is verified
91
90
 
92
- **Certification Level**: Airport-Grade / Mission Critical
91
+ Each behaviour below has a probe in `audit/evaluations/` that drives ScriptPlan
92
+ through its own API or CLI and asserts the result. They run as part of the test
93
+ suite, and can be run on their own against an installed release:
93
94
 
94
- **Capabilities Verified**:
95
-
96
- - **Temporal Physics**: Floating point precision, Timezones, DST awareness, Date Line crossing.
97
-
98
- - **Resource Constraints**: Daily/Weekly limits, Hierarchical quotas, Shift intersections.
99
-
100
- - **Advanced Scheduling**: ALAP (Backward pass), Priority preemption, Smart Resource Selection (Failover).
101
-
102
- - **Workflow Logic**: Atomicity (Contiguous), Perishability (Max Gap), Zero-Buffer Synchronization.
103
-
104
- ## Features
105
-
106
- - Minute-level scheduling precision
107
- - ASAP and ALAP scheduling modes
108
- - Resource allocation with contention handling
109
- - Working hours and shift definitions
110
- - Dependency chains with gap constraints
111
- - Leap year and timezone handling
112
-
113
- ## Accuracy
114
-
115
- ScriptPlan uses integer arithmetic for all time calculations, avoiding floating-point drift. The scheduler correctly handles:
116
-
117
- - Non-standard shift boundaries (e.g., 08:13 - 11:59, 13:07 - 17:47)
118
- - Prime-number effort durations with prime-number gaps
119
- - Resource contention across multi-day tasks
120
- - Calendar gaps (weekends, holidays) vs working time gaps
95
+ ```bash
96
+ audit/evaluations/run_all.sh
97
+ ```
121
98
 
122
- Example: A chain of 500 tasks, each with 73-minute effort and 29-minute gaps, scheduled across leap year boundaries with non-standard shifts, produces exact minute-aligned results.
99
+ **Time**
100
+
101
+ - Dates are read in the project's timezone and scheduled in UTC; reports convert back.
102
+ - Resource shifts follow their own timezone across daylight-saving changes in both directions.
103
+ - A working-hours interval that crosses midnight applies to the following day.
104
+ - Slot size is derived from the times a project uses, so `start 09:30` is honoured
105
+ exactly and a shift of `08:13 - 11:59, 13:07 - 17:47` yields its true 8h26m a day.
106
+ `timingresolution` overrides this.
107
+
108
+ **Units**
109
+
110
+ - Calendar time (`duration`, `gapduration`) and working time (`effort`, `length`,
111
+ `gaplength`) are distinguished, with working time derived from
112
+ `dailyworkinghours` and `yearlyworkingdays`. `m` means months; `min` means minutes.
113
+
114
+ **Resources**
115
+
116
+ - `effort` is person-time: two resources available in a slot contribute two
117
+ slots of it. `mandatory` requires a resource for the task to progress at all;
118
+ `persistent` keeps the one first chosen; `alternative` falls back.
119
+ - `dailymax`, `weeklymax`, `monthlymax`, `maximum` and their `min` counterparts
120
+ block at the cap **and** release in the next period. Periods are counted in the
121
+ resource's own calendar, and a cap smaller than one slot is honoured exactly.
122
+
123
+ **Scheduling**
124
+
125
+ - ASAP and ALAP, including chains that mix the two.
126
+ - Contention is resolved by priority, then criticalness, then declaration order.
127
+ - The declared project period is never rewritten. A task that does not fit is
128
+ reported, with its name and the reason.
129
+ - Scheduling the same project twice produces the same dates and the same bookings.
130
+ - `maxgapduration`, `maxgaplength`, `maxstart`, `minstart`, `maxend` and `minend`
131
+ are checked once the schedule is complete, and a violation is an error.
132
+
133
+ **Honesty**
134
+
135
+ - A project that cannot be scheduled exits non-zero and says why. A dependency
136
+ cycle is rejected before scheduling, naming the cycle.
137
+ - An attribute or reference the parser cannot place is an error, never a silent
138
+ drop.
139
+ - `report_id` is a SHA-256 of the rendered result, so it differs when the
140
+ schedule differs.
141
+
142
+ ## Known limits
143
+
144
+ - `include`, `supplement`, `leaveallowance` and `statussheet` are not implemented.
145
+ - Output formats are JSON and CSV. `ical`, `tjp`, `niku` and MS Project XML are
146
+ rejected at parse time rather than silently producing nothing.
147
+ - Accounts are parsed but not yet modelled, so `chargeset` and `charge` record an
148
+ amount without posting it anywhere.
149
+ - `onstart` dependencies are read as "the dependent task must finish before the
150
+ predecessor starts", which is ScriptPlan's own reading rather than
151
+ TaskJuggler's start-to-start one.
123
152
 
124
153
  ## Example Project
125
154
 
@@ -1,10 +1,11 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0", "wheel", "cython>=3.0.0"]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "scriptplan"
7
- version = "0.9.2"
7
+ # Single source of truth: scriptplan/__init__.py
8
+ dynamic = ["version"]
8
9
  description = "A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management."
9
10
  authors = [
10
11
  { name = "Farshid Ashouri", email = "farsheed.ashouri@gmail.com" },
@@ -50,7 +51,6 @@ dev = [
50
51
  "twine",
51
52
  "ruff>=0.1.0",
52
53
  "mypy>=1.0.0",
53
- "cython>=3.0.0",
54
54
  ]
55
55
 
56
56
  [project.scripts]
@@ -62,23 +62,29 @@ plan = "scriptplan.cli.plan:main"
62
62
  "Documentation" = "https://github.com/rodmena-limited/scriptplan#readme"
63
63
  "Bug Tracker" = "https://github.com/rodmena-limited/scriptplan/issues"
64
64
  "Source" = "https://github.com/rodmena-limited/scriptplan"
65
+ "Changelog" = "https://github.com/rodmena-limited/scriptplan/blob/main/CHANGELOG.md"
65
66
 
66
67
  [tool.setuptools]
67
68
  include-package-data = true
68
69
 
70
+ [tool.setuptools.dynamic]
71
+ version = { attr = "scriptplan.__version__" }
72
+
69
73
  [tool.setuptools.packages.find]
70
74
  where = ["."]
71
75
  include = ["scriptplan*"]
72
76
 
73
77
  [tool.setuptools.package-data]
74
- scriptplan = ["py.typed", "parser/*.lark", "_cython/*.pyx"]
78
+ scriptplan = ["py.typed", "parser/*.lark"]
75
79
 
76
80
  [tool.pytest.ini_options]
77
81
  testpaths = ["tests"]
78
82
  python_files = ["test_*.py"]
79
83
  python_classes = ["Test*"]
80
84
  python_functions = ["test_*"]
81
- addopts = "-v --tb=short -n auto"
85
+ # No -n here: pytest-xdist is an optional extra, and requiring it means
86
+ # the suite cannot start without it. Pass -n yourself to parallelise.
87
+ addopts = "--tb=short"
82
88
  filterwarnings = [
83
89
  "ignore::DeprecationWarning",
84
90
  "ignore::PendingDeprecationWarning",
@@ -9,7 +9,9 @@ the pioneering open-source project management tool created by Chris Schlaeger.
9
9
  The TJP file format and scheduling concepts originate from TaskJuggler.
10
10
  """
11
11
 
12
- __version__ = "0.9.2"
12
+ # The single source of truth for the version; pyproject reads it from here.
13
+ # 0.10.1: tasks sharing a slot share its time instead of overbooking it.
14
+ __version__ = "0.10.1"
13
15
  __author__ = "ScriptPlan Team"
14
16
 
15
17
  from scriptplan.core.project import Project