5.9 KiB
5.9 KiB
1. Package scaffolding
- 1.1 Create the
world/package (__init__.py,models.py,model.py,config.py) - 1.2 Add
world*to[tool.setuptools.packages.find].includeinpyproject.toml(no new third-party dependency) - 1.3 Add World Runtime configuration in
world/config.py: an enabled/disabled flag (default enabled) and a history-size bound (default 10), sourced from environment/config in one placeworld/reads from - 1.4 Extend the project's smoke test (that imports every package) to import
world
2. WorldState data model (capability: world-model)
- 2.1 Implement
world/models.py:WorldEvent(scene_summary: SemanticScene | Scene,action: str,success: bool,timestamp: datetime) andWorldState(current_app: str | None,current_page: str | None,variables: dict[str, Any],history: deque[WorldEvent]) dataclasses, withto_dict()mirroring the style ofcore/models.py(for future inspection/debugging use, not persistence in this change) - 2.2 Implement
WorldState.historyas acollections.deque(maxlen=<configured bound>)so oldest entries are evicted automatically once the bound is exceeded - 2.3 Write unit tests for
WorldState/WorldEventconstruction and forhistory's bounded-eviction behavior (append past the configuredmaxlenand assert the oldest entry is gone, length stays at the bound)
3. WorldModel update hook (capability: world-model)
- 3.1 Implement
world/model.py:WorldModelowning oneWorldStateper task, withobserve(scene: Scene, semantic_scene: SemanticScene | None, step: PlannedStep, result: StepResult) -> Noneas the single update entry point - 3.2 Implement the
current_pageupdate rule: setcurrent_page = semantic_scene.pagewhensemantic_sceneis notNoneand itspageis non-empty; leave unchanged otherwise - 3.3 Implement the
current_appupdate rule: on a successfulStepResultforstep.action in {"launch_app", "terminate_app"}, set/clearcurrent_appfromstep.args(e.g.bundle_id/app_id); leave unchanged for any other action or a failed result - 3.4 Implement the
variablesupdate rule: mergestep.args["remember"](adict) intoWorldState.variableswhen present; leavevariablesunchanged when absent - 3.5 Implement the
historyupdate rule: append oneWorldEventper call toobserve(), usingsemantic_scenewhen available and falling back tosceneotherwise - 3.6 Make every update rule defensive: missing/malformed expected fields (e.g. no
bundle_idon alaunch_appstep, non-dictremembervalue) are logged and skipped, never raised, soobserve()never raises for any input shape - 3.7 Write unit tests for
WorldModel.observe()covering: page update fromSemanticScene, page unchanged whensemantic_sceneisNone, app update on successfullaunch_app, app unchanged on failedlaunch_appor unrelated actions,variablesmerge viaremember,variablesunchanged withoutremember, and history append/eviction across repeatedobserve()calls
4. TaskContext and TaskRunner integration (capability: world-model)
- 4.1 Add
world: WorldState | None = Nonefield toTaskContextinruntime/context.py - 4.2 Add an optional
world_model: WorldModel | None = Noneconstructor argument toTaskRunnerinruntime/task.py; whenNoneand World Runtime is enabled in config, construct a defaultWorldModelinternally; when World Runtime is disabled in config, leavecontext.worldasNoneand skip the update hook entirely - 4.3 In
TaskRunner.run()'s step loop, callworld_model.observe(scene, semantic_scene, step, result)once per executed step, immediately after the existingcontext.add_step_result(result)line, and refreshcontext.worldfrom the model's currentWorldState - 4.4 Confirm
TaskRunner.run()'s existing control flow (retry/failure/max-steps handling) is unaffected by the new hook call — the hook must never change whether a step is treated as success/failure - 4.5 Write unit tests for
TaskRunnercovering:context.worldpopulated after a step when World Runtime is enabled (default),context.worldremainingNonewhen explicitly disabled via config, and a task run completing normally (unchanged pass/fail outcome) whether World Runtime is enabled or disabled
5. Planner integration (capability: world-model)
- 5.1 Add an optional
world: WorldState | None = Nonekeyword argument toPlanner.plan()inruntime/planner.py; the existing stubPlannerimplementation accepts but does not use it - 5.2 Update
TaskRunner.run()'s call toself.planner.plan(...)to passworld=context.world - 5.3 Write a unit test asserting the existing stub
Planner.plan()call sites (with and without aworldargument) both continue to return the same steps as before this change - 5.4 Write a unit test asserting
TaskRunnerpasses the currentcontext.worldintoPlanner.plan()'sworldargument by the second step of a multi-step task (using a custom testPlannersubclass that records theworldvalue it was given)
6. End-to-end validation
- 6.1 Write an end-to-end test running a multi-step task through
TaskRunnerwith a mockedDriver/Scene/SemanticScenesequence, assertingWorldState.current_app/current_page/historyreflect the expected values after each step - 6.2 Write an end-to-end test confirming World Runtime tracking failure modes (missing
SemanticScene, missing expected step args) never fail or interrupt the task loop, only leave the correspondingWorldStatefield unchanged - 6.3 Confirm World Runtime tracking is enabled by default after applying this change, and that disabling it via config fully restores pre-this-change
TaskContext/Planner.plan()call behavior (noworldstate populated or passed) - 6.4 Run the full test suite (
pytest) and confirm no existing test intests/needed a behavior change, only additive new tests