Skip to content

feat: thread instrument_capabilities through TaskData/TaskArchive/scheduler (§A.2-A.7) - #865

Merged
thusser merged 1 commit into
developfrom
instrument-capabilities-wiring
Sep 2, 2026
Merged

feat: thread instrument_capabilities through TaskData/TaskArchive/scheduler (§A.2-A.7)#865
thusser merged 1 commit into
developfrom
instrument-capabilities-wiring

Conversation

@thusser

@thusser thusser commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

§A.2-A.7 of specs/plans/2026-09-01-instrument-capability-duration-estimates.md — the plumbing that carries an InstrumentCapabilities | None (from pyobs-core#864) from the one real caller down to Task.estimate_duration(), with no consumer yet and every backend still returning None, so this changes no runtime behavior.

  • TaskData.instrument_capabilities: InstrumentCapabilities | None (task.py)
  • TaskArchive.get_instrument_capabilities() -> None by default; every non-portal backend (filesystem, memory, lco) inherits it unchanged
  • Task.estimate_duration() gains the parameter, forwarded into TaskData
  • TaskScheduler.schedule() gains it in the abstract signature. AstroplanScheduler accepts it for interface consistency but doesn't use it (reads the stored Task.duration field, not a live estimate — see the plan's Non-goals)
  • OnDemandScheduler threads it through the full call chain: scheduleschedule_in_intervalschedule_first_in_intervalfind_next_best_task/check_for_better_task/can_postpone_task/create_scheduled_task → the 4 task.estimate_duration() call sites
  • pyobs/modules/robotic/scheduler.py's _schedule_worker — the only real caller of schedule() outside tests — now supplies self._task_archive.get_instrument_capabilities()

Not in this PR

  • §A.4: PortalTaskArchive's concrete fetch/cache override (still returns None via the base class default)
  • §A.8: the 5 leaf scripts actually reading instrument_capabilities

Both tracked in the plan doc as follow-up work.

Test plan

  • New tests: TaskData/Task.estimate_duration() forwarding, TaskArchive default, OnDemandScheduler.create_scheduled_task() + full schedule() end-to-end threading, pyobs/modules/robotic/scheduler.py's _schedule_worker forwarding get_instrument_capabilities()
  • pytest tests/ (excluding integration/xmpp) — 1866 passed, no regressions
  • ruff check / black --check / pyrefly check — clean (pre-existing, unrelated pyrefly noise on 2 test files confirmed identical on develop)

🤖 Generated with Claude Code

https://claude.ai/code/session_019XoNCe7YyJELc8xup1zVdE

…eduler (§A.2-A.7)

- TaskData gains instrument_capabilities: InstrumentCapabilities | None
  (task.py)
- TaskArchive gains get_instrument_capabilities() -> None by default;
  every non-portal backend inherits it unchanged (taskarchive.py)
- Task.estimate_duration() gains an optional instrument_capabilities
  parameter, forwarded into TaskData
- TaskScheduler.schedule() gains the parameter in its abstract signature.
  AstroplanScheduler accepts it for interface consistency but doesn't use
  it (reads the stored Task.duration field, not a live estimate -- see
  the plan's Non-goals). OnDemandScheduler threads it through the full
  call chain (schedule -> schedule_in_interval -> schedule_first_in_interval
  -> find_next_best_task/check_for_better_task/can_postpone_task/
  create_scheduled_task -> the 4 task.estimate_duration() call sites)
- pyobs/modules/robotic/scheduler.py's _schedule_worker, the only real
  caller of schedule() outside tests, supplies
  self._task_archive.get_instrument_capabilities()

§A.2-A.7 of specs/plans/2026-09-01-instrument-capability-duration-estimates.md.
PortalTaskArchive's concrete fetch/cache override (§A.4) and the 5 leaf
scripts reading instrument_capabilities (§A.8) are follow-up work -- every
backend still returns None today, so this PR changes no runtime behavior,
only plumbing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XoNCe7YyJELc8xup1zVdE

@thusser thusser left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: approve — clean, complete §A.2–A.7 plumbing

I traced the full call chain, checked interface containment across the repo and sibling projects, and verified the "no runtime behavior change" claim. No blockers; minor notes below.

Verified

  • Threading is complete. All four estimate_duration() call sites in pyobs/robotic/scheduler/ondemandscheduler.py (create_scheduled_task, evaluate_constraints_and_merits, check_for_better_task, can_postpone_task) receive the parameter, and the recursive chain schedule → schedule_in_interval → schedule_first_in_interval threads it in both directions, including the "between-tasks" recursion. The run_cpu_bound positional calls are consistent.
  • No behavior change — claim holds. get_instrument_capabilities() defaults to None and every backend inherits it: FileSystemTaskArchive/Yaml, MemoryTaskArchive (tested), LcoTaskArchive, and PortalTaskArchive (future §A.4 override). Nothing reads the new field yet, AstroplanScheduler genuinely ignores it, and Task.estimate_duration() without caps produces an identical TaskData to before.
  • Interface change is contained. Only two in-repo TaskScheduler subclasses exist (both updated) and exactly one real caller of schedule() (pyobs/modules/robotic/scheduler.py's _schedule_worker). Sibling repos: no scheduler subclasses; pyobs-task-editor calls task.estimate_duration() zero-arg — unaffected. All Script.estimate_duration overrides take (data, time) positionally, so the unchanged script call convention holds.
  • Tooling: 119/119 tests pass across the four touched test files (see the IERS note below); ruff and black clean; no import cycles (smoke-imported every touched module). pyrefly reports 24 errors, all in the two pre-existing test files — confirmed identical on develop (same error set, line numbers shifted by the PR's insertions).

Notes (non-blocking)

  1. Out-of-tree TaskScheduler subclasses would break at runtime. Any site-specific scheduler (config class-string pattern) overriding schedule(tasks, projects, start, end) without the new kwarg raises TypeError once driven by the updated module. §A.6 deliberately accepts this interface change and nothing in the checked sibling repos subclasses it — but a CHANGELOG/release note would be prudent.
  2. One test is environment-dependent (not a code issue): test_schedule_threads_instrument_capabilities_end_to_end does real Observer/time math and needs the astropy IERS table — it fails in a sandbox with a read-only ~/.astropy cache only, and passes with auto_max_age = None. Fine on networked CI; it just joins the existing IERS-dependent tests.
  3. Nits: _CapabilitiesEchoingScript is duplicated across test_task.py and test_ondemandscheduler.py (a conftest helper would DRY it); the module-level forwarding test's fake_schedule + CancelledError-by-sleep-count trick is clever but convoluted — acceptable since it matches the file's existing test patterns.
  4. The relocated # type: ignore[arg-type] on the schedule_first_in_interval call is still effective (pyrefly clean on the file).

Summary

Plumbing is complete, correct, and genuinely behavior-neutral; tests cover the forwarding at each meaningful layer (module → scheduler → create_scheduled_task, TaskData → script, archive default). Merge as-is and carry note 1 into the §A.4/A.8 follow-ups.

@thusser
thusser merged commit cc19752 into develop Sep 2, 2026
4 checks passed
@thusser
thusser deleted the instrument-capabilities-wiring branch September 2, 2026 19:03
thusser added a commit that referenced this pull request Sep 3, 2026
…emented

§A landed across pyobs-core#864/#865/#867/#868, released as pyobs-core
v2.4.0. §B landed in pyobs-portal#144/#145 (dependency bumped, real path
now exercised end-to-end, 0 skipped). All test-plan items checked off
except the manual script-builder browser click-through, left open --
automated coverage exercises the same code path but nobody's watched it
in a browser yet.

specs/plans/index.md's entry marked implemented; fleet-open-items.md's
plan entries removed per its own maintenance rule (implemented items
don't stay, they get removed outright), #858/#859's dangling "plan below"
reference reworded since the plan itself is no longer listed there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XoNCe7YyJELc8xup1zVdE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant