Working contract for contributors/agents. Preserve decisions here unless explicitly changed.
PyQt6 desktop annotation tool for OSL sports-video datasets.
Must support:
- Project lifecycle: create/open/close/save/export JSON.
- Dataset curation: samples, metadata, schema (
labels). - Modes: Classification, Localization, Description, Dense Description.
- Editing/review UX: tree + timeline + table + media controls + filter + undo/redo.
Data model target:
- Root: metadata +
labels+data. - Sample:
inputsand task keys likelabels,events,captions,dense_captions.
Layers:
- UI (
annotation_tool/ui/...): presentation + intent signals. - Controllers (
annotation_tool/controllers/...): behavior/state orchestration. - Composition (
annotation_tool/main_window.py): only place for cross-module wiring.
Runtime owners:
DatasetExplorerController: canonical in-memorydataset_json, lifecycle, tree/filter/selection.HistoryManager: tracked mutations + undo/redo authority.MediaController: playback route/state/mute authority.- Mode controllers (
Classification,Localization,Description,Dense): mode-local behavior; emit mutation/media intents.
Default signal flow:
- UI emits intent.
- Controller emits typed request signal.
MainWindow.connect_signals()routes to owner.- Owner mutates state and emits refresh/context.
- No controller may store/accept
main_window. - No direct controller-to-controller constructor coupling in scoped modules.
- No event bus.
- No
DatasetModelFacade. - No legacy aliases (
window.model,window.router); usewindow.dataset_explorer_controller. - Mode controllers should only receive their panel.
- Explorer/editor controllers must not use
QMediaPlayerdirectly. - Do not duplicate tracked mutation logic outside
HistoryManager.
- Canonical document:
DatasetExplorerController.dataset_json. - Tracked edits flow through
HistoryManager.execute_*. - Effective mutation => exactly 1 undo entry.
- No-op mutation => 0 undo entries.
- Forward mutation clears redo.
- Undo/redo must restore structural JSON equality.
- Tab switch must not repopulate tree.
- Tab switch with same selection/path must not restart media.
- Undo/redo should prefer lightweight refresh over full rebuild when possible.
- If filter hides selected row: clear selection (do not auto-select first visible).
- Explicit user sample/input selection should ensure playback when needed.
- No single-view vs multi-view creation prompt.
- No
is_multi_viewnew-project branch. - Description controller consumes selected
sampleand emits caption-only updates. - Classification manual edits save immediately on effective change.
- Dense add remains explicit modal (
Add New Description), edits table-driven. - Mute control: icon button on right side of timeline row.
- Prefer simple, explicit logic.
- Keep one canonical implementation per operation.
- Delete legacy paths instead of adding shims.
- Keep constructors minimal/explicit.
- Remove unused functions after refactors.
- Remove redundant logic/helpers.
For architecture, mutation, playback, or workflow changes, update/run relevant suites in the folder tests/gui. Always run relevant tests before considering work complete.
- Controller boundaries preserved?
- History 1-or-0 push rule preserved?
- No unnecessary tree/media regressions?
- New behavior wired through
MainWindow.connect_signals()? - No legacy aliases/facades reintroduced?
- Logic kept simple?
- Unused functions removed?
- Redundant logic removed and canonical path retained?
- README/docs updated if public contract changed?
- Relevant regression tests added/updated and run?