ecs: std/ecs schedule: System, Stage, Schedule and the ecs-schedule gate (step 2 of #89) #763
Labels
No labels
loop
queue/failed
queue/queued
ready
status/parked
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
torbscript/language#763
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Step 2 of 3 of #89 (ECS slice 1). The specification is ECS.md section 6 ("The access set is declared", "Stages and the fixed step") and the owner's decisions in section 15 (q3: no
Worldinstd/ecs; q8: the frame loop lives instd/scene,std/ecsknows onlySchedule.run). Step 1 (#453, merged) shipped the data layer instd/ecsincludingComponentKeyas a hand-written string. This step adds the systems, stages and schedule to the same package.std/transform(step 3) is not part of it.Goal
System<World>,Stage,Schedule<World>instd/ecswith a sequential runner, a fixed-step accumulator overDurationand a conflict diagnostic, and the gate of slice 1: a conformance program that steps a world through a schedule for several frames and prints the same bytes in the VM and natively.Depends on
#453 (merged). Nothing unpublished;
std/time(Duration) exists onmain.std/ecsgains a dependency onstd/timein itsproject.trb. No seed impact: no syntax, no native, no driver flag, no std name the compiler looks up by string.Requirements
public type System<World>withname: String,reads: Set<ComponentKey> = [],writes: Set<ComponentKey> = []andrun: (var world: World, elapsed: Duration) => Void, as in ECS.md section 6. The access sets are a documented promise; the checker does not verify them.Stageas a value naming a stage: the four built-inStage.input,Stage.fixed,Stage.update,Stage.late, in that run order, andStage(name:)for a user stage.Schedule<World>shall hold the stages in run order and the systems of each stage in declaration order;var fn add(system: System<World>, to stage: Stage)appends a system, andvar fn addStage(stage: Stage, after other: Stage)inserts a user stage (an unknownotherpanics naming it). Declaration order is the only order; no sorting, no hashing of iteration order.Schedule.run(var world: World, elapsed: Duration)is called, the schedule shall runInput, then addelapsedto the accumulator and runFixedonce per wholestepcontained in it (zero or more times, each withelapsed = step, subtractingstepeach time), thenUpdate, thenLate, and user stages at their position.Input,Update,Lateand user stages receive the frame'selapsed.Duration(exact count, noFloat);stepdefaults to 16 ms and is a field;stepof zero or less panics atrun(it would not terminate). The remainder after a frame stays in the accumulator and carries into the next frame. A cap ofmaxStepsPerFrame: Int = 8bounds the catch-up: more whole steps than the cap drop the excess from the accumulator (the spiral-of-death guard of Bevy and Unity) and the dropped count is readable asdroppedSteps.writes, or one writes a key the other reads,Schedule.conflicts(): List<Conflict>shall list the pair (Conflictwithstage,first,second,key,Equals, Show); the runner never refuses to run and never reorders because of it (ECS.md: until the fork-join barrier exists the access sets are checked for a conflict and otherwise only documented). Systems in different stages never conflict.elapsedsequence give the same system calls in the same order on every machine and in both back ends.# Examplesblock; noWorldtype, resource or frame loop is shipped (ECS.md q3, q8).Decisions (by the loop, refinement)
Stageis a struct value, not an enum, because a program adds its own stages (ECS.md: "a program may add its own"); the four built-ins arepublic constmembers, equality by name. A stage added twice or a system added to an unknown stage panics naming it.conflicts(), not atadd, so a program can add systems in any order and a test can pin the exact pair list. ASchedule.check()that panics on a non-empty list is deliberately not added now; slice 4 (key fromcomponentKey<>(), ECS.md section 13) owns turning the report into a diagnostic.elapsedof an hour runs the fixed stage 225 000 times; the cap is the standard answer and costs one field.maxStepsPerFrameis the loop's technical choice, not a product question; it is a field a program may raise.Fixedsystems getelapsed = step(not the frame's), so a fixed system is a pure function of the step count, which is what the lockstep rule of ECS.md section 3 "Determinism" needs.std/ecs/src/system.trb(System,Conflict),stage.trb(Stage),schedule.trb(Schedule), re-exported fromlib.trb; tests instd/ecs/tests/schedule.test.trb.ComponentKeyfrom #453 (ComponentKey("acme/game/Position"));componentKey<T>()is #90.Acceptance criteria
std/ecshasSystem,Stage,Schedule,Conflictas above;std/ecs/project.trbdepends onstd/time;torb check std/ecsandtorb format --checkare clean;tools/check-commits.shclean.Input,Fixed,Update,Lateand one user stage addedaftera built-in shows the exact order; two systems in one stage run in declaration order.step = 10 ms, frames of 4, 4, 4, 25 and 0 ms give fixed-run counts 0, 0, 1, 2, 0 (accumulator after each frame 4, 8, 2, 7, 7 ms, pinned in the test), fixed systems seeelapsed == step, the others the frame'selapsed; the accumulator after each frame equals the expectedDurationexactly (a test of 3 600 frames of equalelapsedshows no drift: total fixed time equalsstep * count + remainder).elapsedof 1 s withstep = 10 msandmaxStepsPerFrame = 8runsFixed8 times, setsdroppedStepsto 92 and leaves the accumulator belowstep;step <= 0panics (a.trbtest or a conformance program with.exit/.stderr).conflicts(): write/write and write/read pairs of one stage are reported with stage, both system names and the key, in declaration order; read/read, different stages and disjoint keys report nothing; running a conflicting schedule still runs every system.tests/conformance/ecs-schedule.trbdeclares its ownWorld(twoColumnsgroups as inecs-data), three to four systems acrossInput,Fixed,UpdateandLate(one writing, one reading the same key in a later stage), a fixed step, and steps the world for at least eight frames with unevenelapsedvalues, printing the world and the system log after each frame plusconflicts()of a deliberately conflicting second schedule. Tier B runs it in the VM and natively and both must giveecs-schedule.expectedbyte for byte; the.allocationsbudget is taken from the first forge CI run's report; a leak run is clean.docs/standard-library/ecs.mdgains the schedule section (the frame-loop pattern of ten lines, labelled as the program's own, q8);skills/regenerated (torb docs skill docs skills,--checkis a tier A gate); ECS.md section 13 slice 1 marked "data layer and schedule landed" with #453 and this issue; #89 step 2 ticked by the PR text (Refs #89).Seed impact
None (see "Depends on").
Size and model
One PR, two Conventional Commits:
feat(std/ecs): systems, stages and the sequential schedule(package, tests, conformance program) anddocs(std/ecs): schedule page, skills and the design record. Implementer: sonnet, medium (std/package, no compiler change).Part of #89 (
Refs #89, notCloses).std/ecswith oneStoreper group type, andstd/transform(ECS slice 1) #89std/ecswith oneStoreper group type, andstd/transform(ECS slice 1) #89[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T21:09Z
[loop:torbscript] implement: #769, head
ca829a915c, CI pending (gates, tier A, tier B still queued after 28 min), next: waiting (CI and review)[loop:torbscript] release implement: CI still queued after the 30 minute wait, nothing more to do until it runs; tier B may need tests/conformance/ecs-schedule.allocations (no native build locally)
std/ecswith oneStoreper group type, andstd/transform(ECS slice 1) #89std/scene(ECS slices 5, 7 and 8) #91[loop:torbscript] claim review by session:loop-torbscript-20261113 until 2026-11-14T01:10:00Z
Bufferand parallel systems (ECS slices 6 and 9) #92std/tensoron the CPU #82[post-merge] PR #769 merged (
c4e91a10c, fixes on3e3c127c3). Nothing live to verify: std/ecs is library code, the nightly-20261113 prerelease already carries it. Release step not done: no x.y.z tag exists yet (tags are nightly-YYYYMMDD only) and project.trb is still version 0.1.0, so release.yml (tag-triggered) has nothing to publish. Next step: version-bump PR on main (project.trb version, compiler/project.trb, compiler/src/package/version.trb languageVersion, editors/vscode/package.json), reviewer argus requested; after it is merged, tag the chosen x.y.z (a feature: minor bump) and check the release and images jobs. Owner: orion.std/transform2D:Transform2,GlobalTransform2and propagation (step 3a of #89) #779std/ecswith oneStoreper group type, andstd/transform(ECS slice 1) #89