groundskeeping 0.1.0__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 (65) hide show
  1. groundskeeping-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +66 -0
  2. groundskeeping-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. groundskeeping-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +32 -0
  4. groundskeeping-0.1.0/.github/ISSUE_TEMPLATE/not_working.yml +65 -0
  5. groundskeeping-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +9 -0
  6. groundskeeping-0.1.0/.github/release-drafter.yml +31 -0
  7. groundskeeping-0.1.0/.github/workflows/ci.yml +10 -0
  8. groundskeeping-0.1.0/.github/workflows/docs.yml +10 -0
  9. groundskeeping-0.1.0/.github/workflows/merge.yml +13 -0
  10. groundskeeping-0.1.0/.github/workflows/publish.yml +21 -0
  11. groundskeeping-0.1.0/.gitignore +228 -0
  12. groundskeeping-0.1.0/CONTRIBUTING.md +27 -0
  13. groundskeeping-0.1.0/PKG-INFO +133 -0
  14. groundskeeping-0.1.0/README.md +116 -0
  15. groundskeeping-0.1.0/docs/actions.md +108 -0
  16. groundskeeping-0.1.0/docs/api/actions.md +3 -0
  17. groundskeeping-0.1.0/docs/api/app.md +3 -0
  18. groundskeeping-0.1.0/docs/api/configurator.md +5 -0
  19. groundskeeping-0.1.0/docs/api/jobs.md +3 -0
  20. groundskeeping-0.1.0/docs/api/pages.md +3 -0
  21. groundskeeping-0.1.0/docs/api/telemetry.md +7 -0
  22. groundskeeping-0.1.0/docs/api/views.md +3 -0
  23. groundskeeping-0.1.0/docs/api/widgets.md +7 -0
  24. groundskeeping-0.1.0/docs/configuration.md +56 -0
  25. groundskeeping-0.1.0/docs/contributing.md +47 -0
  26. groundskeeping-0.1.0/docs/index.md +55 -0
  27. groundskeeping-0.1.0/docs/ownership.md +55 -0
  28. groundskeeping-0.1.0/docs/pages.md +72 -0
  29. groundskeeping-0.1.0/docs/quickstart.md +77 -0
  30. groundskeeping-0.1.0/docs/telemetry.md +46 -0
  31. groundskeeping-0.1.0/examples/demo_app.py +6 -0
  32. groundskeeping-0.1.0/mkdocs.yml +79 -0
  33. groundskeeping-0.1.0/pyproject.toml +51 -0
  34. groundskeeping-0.1.0/src/groundskeeping/__init__.py +17 -0
  35. groundskeeping-0.1.0/src/groundskeeping/app.py +233 -0
  36. groundskeeping-0.1.0/src/groundskeeping/configurator/__init__.py +18 -0
  37. groundskeeping-0.1.0/src/groundskeeping/configurator/adapter.py +227 -0
  38. groundskeeping-0.1.0/src/groundskeeping/configurator/models.py +110 -0
  39. groundskeeping-0.1.0/src/groundskeeping/contracts/__init__.py +57 -0
  40. groundskeeping-0.1.0/src/groundskeeping/contracts/actions.py +360 -0
  41. groundskeeping-0.1.0/src/groundskeeping/contracts/jobs.py +245 -0
  42. groundskeeping-0.1.0/src/groundskeeping/contracts/pages.py +112 -0
  43. groundskeeping-0.1.0/src/groundskeeping/contracts/views.py +111 -0
  44. groundskeeping-0.1.0/src/groundskeeping/demo.py +255 -0
  45. groundskeeping-0.1.0/src/groundskeeping/navigation.py +21 -0
  46. groundskeeping-0.1.0/src/groundskeeping/telemetry/__init__.py +10 -0
  47. groundskeeping-0.1.0/src/groundskeeping/telemetry/contracts.py +17 -0
  48. groundskeeping-0.1.0/src/groundskeeping/telemetry/models.py +55 -0
  49. groundskeeping-0.1.0/src/groundskeeping/telemetry/providers/__init__.py +3 -0
  50. groundskeeping-0.1.0/src/groundskeeping/telemetry/providers/fake.py +80 -0
  51. groundskeeping-0.1.0/src/groundskeeping/telemetry/runtime.py +72 -0
  52. groundskeeping-0.1.0/src/groundskeeping/theme.py +67 -0
  53. groundskeeping-0.1.0/src/groundskeeping/themes/groundskeeping.tcss +148 -0
  54. groundskeeping-0.1.0/src/groundskeeping/widgets/__init__.py +4 -0
  55. groundskeeping-0.1.0/src/groundskeeping/widgets/configurator.py +39 -0
  56. groundskeeping-0.1.0/src/groundskeeping/widgets/primitives.py +50 -0
  57. groundskeeping-0.1.0/src/groundskeeping/widgets/telemetry.py +26 -0
  58. groundskeeping-0.1.0/src/groundskeeping/widgets/workbench.py +236 -0
  59. groundskeeping-0.1.0/tests/test_actions_and_jobs.py +102 -0
  60. groundskeeping-0.1.0/tests/test_app_smoke.py +18 -0
  61. groundskeeping-0.1.0/tests/test_configurator_adapter.py +93 -0
  62. groundskeeping-0.1.0/tests/test_dependency_boundaries.py +35 -0
  63. groundskeeping-0.1.0/tests/test_navigation_contracts.py +29 -0
  64. groundskeeping-0.1.0/tests/test_telemetry_core.py +43 -0
  65. groundskeeping-0.1.0/uv.lock +1029 -0
@@ -0,0 +1,66 @@
1
+ name: "🐞 Bug Report"
2
+ description: Report a reproducible bug.
3
+ type: "Bug"
4
+ body:
5
+ - type: checkboxes
6
+ attributes:
7
+ label: Pre-flight
8
+ options:
9
+ - label: I searched existing issues and this is not a duplicate.
10
+ required: true
11
+
12
+ - type: textarea
13
+ id: summary
14
+ attributes:
15
+ label: Summary
16
+ description: One or two sentences describing the bug.
17
+ validations:
18
+ required: true
19
+
20
+ - type: textarea
21
+ id: reproduction
22
+ attributes:
23
+ label: Reproduction
24
+ description: Steps and/or a minimal code example that reproduces the issue. Wrap code in triple backticks.
25
+ placeholder: |
26
+ 1. Import X and call Y with Z
27
+ 2. Observe error
28
+
29
+ ```python
30
+ # minimal example
31
+ ```
32
+ validations:
33
+ required: true
34
+
35
+ - type: textarea
36
+ id: actual
37
+ attributes:
38
+ label: Actual behaviour
39
+ validations:
40
+ required: true
41
+
42
+ - type: textarea
43
+ id: expected
44
+ attributes:
45
+ label: Expected behaviour
46
+ validations:
47
+ required: true
48
+
49
+ - type: textarea
50
+ id: logs
51
+ attributes:
52
+ label: Error output
53
+ description: Full traceback if applicable. Formatted automatically.
54
+ render: python-traceback
55
+
56
+ - type: textarea
57
+ id: system
58
+ attributes:
59
+ label: System info
60
+ description: |
61
+ Run this in your environment and paste the output:
62
+
63
+ ```shell
64
+ python <(curl -s https://raw.githubusercontent.com/AustralianCancerDataNetwork/cava-devops/main/scripts/cava_system_info.py)
65
+ ```
66
+ render: shell
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Discussions
4
+ url: https://github.com/orgs/AustralianCancerDataNetwork/discussions
5
+ about: Questions and general discussion about the CAVA stack.
@@ -0,0 +1,32 @@
1
+ name: "🚀 Feature Request"
2
+ description: Propose a new feature or enhancement.
3
+ type: "Feature"
4
+ body:
5
+ - type: checkboxes
6
+ attributes:
7
+ label: Pre-flight
8
+ options:
9
+ - label: I searched existing issues and this has not been requested before.
10
+ required: true
11
+
12
+ - type: textarea
13
+ id: problem
14
+ attributes:
15
+ label: Problem or motivation
16
+ description: What are you trying to do that you currently cannot?
17
+ validations:
18
+ required: true
19
+
20
+ - type: textarea
21
+ id: proposal
22
+ attributes:
23
+ label: Proposed solution
24
+ description: How would you like this to work?
25
+ validations:
26
+ required: true
27
+
28
+ - type: textarea
29
+ id: alternatives
30
+ attributes:
31
+ label: Alternatives considered
32
+ description: Other approaches you considered and why you ruled them out.
@@ -0,0 +1,65 @@
1
+ name: "❗ Something is not working"
2
+ description: Something behaves unexpectedly but you are not sure if it is a bug.
3
+ body:
4
+ - type: checkboxes
5
+ attributes:
6
+ label: Pre-flight
7
+ options:
8
+ - label: I searched existing issues for this problem.
9
+ required: true
10
+
11
+ - type: textarea
12
+ id: summary
13
+ attributes:
14
+ label: Problem summary
15
+ description: 1-2 sentences describing what is not working.
16
+ validations:
17
+ required: true
18
+
19
+ - type: textarea
20
+ id: reproduction
21
+ attributes:
22
+ label: Reproduction
23
+ description: Steps and/or a minimal code example. Wrap code in triple backticks.
24
+ placeholder: |
25
+ 1. Import X and call Y with Z
26
+ 2. Observe unexpected behaviour
27
+
28
+ ```python
29
+ # minimal example
30
+ ```
31
+ validations:
32
+ required: true
33
+
34
+ - type: textarea
35
+ id: actual
36
+ attributes:
37
+ label: Actual outcome
38
+ validations:
39
+ required: true
40
+
41
+ - type: textarea
42
+ id: expected
43
+ attributes:
44
+ label: Expected outcome
45
+ validations:
46
+ required: true
47
+
48
+ - type: textarea
49
+ id: logs
50
+ attributes:
51
+ label: Error messages
52
+ description: Full traceback if applicable. Formatted automatically.
53
+ render: python-traceback
54
+
55
+ - type: textarea
56
+ id: system
57
+ attributes:
58
+ label: System info
59
+ description: |
60
+ Run this in your environment and paste the output:
61
+
62
+ ```shell
63
+ python <(curl -s https://raw.githubusercontent.com/AustralianCancerDataNetwork/cava-devops/main/scripts/cava_system_info.py)
64
+ ```
65
+ render: shell
@@ -0,0 +1,9 @@
1
+ ## Summary
2
+
3
+ <!-- What does this PR do? This text becomes the changelog entry — write it for a reader who will see it months from now, not for the reviewer reading the diff. Leave blank for chore PRs. -->
4
+
5
+ ## Checklist
6
+
7
+ - [ ] Applied exactly one label (`breaking`, `feature`, `fix`, `dependencies`, or `chore`)
8
+ - [ ] Tests pass locally (`uv run pytest -q`)
9
+ - [ ] Lint passes (`uv run ruff check .`)
@@ -0,0 +1,31 @@
1
+ name-template: 'v$RESOLVED_VERSION'
2
+ tag-template: 'v$RESOLVED_VERSION'
3
+ commitish: main
4
+
5
+ categories:
6
+ - title: Breaking Changes
7
+ labels: ['breaking']
8
+ - title: Features
9
+ labels: ['feature']
10
+ - title: Fixes
11
+ labels: ['fix']
12
+ - title: Dependencies
13
+ labels: ['dependencies']
14
+
15
+ template: |
16
+ $CHANGES
17
+
18
+ change-template: '- **$TITLE** (#$NUMBER) @$AUTHOR'
19
+
20
+ version-resolver:
21
+ major:
22
+ labels: ['breaking']
23
+ minor:
24
+ labels: ['feature']
25
+ patch:
26
+ labels: ['fix', 'dependencies']
27
+ default: patch
28
+
29
+ exclude-labels: ['chore']
30
+
31
+ autolabeler: []
@@ -0,0 +1,10 @@
1
+ name: CI
2
+ on:
3
+ pull_request:
4
+ branches: [main]
5
+ types: [opened, synchronize, reopened, labeled, unlabeled]
6
+ jobs:
7
+ label-gate:
8
+ uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/label-gate.yml@main
9
+ build-test:
10
+ uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/build-test.yml@main
@@ -0,0 +1,10 @@
1
+ name: Deploy Docs
2
+ on:
3
+ push:
4
+ tags: ['v*']
5
+ workflow_dispatch:
6
+ permissions:
7
+ contents: write
8
+ jobs:
9
+ deploy:
10
+ uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/deploy-docs.yml@main
@@ -0,0 +1,13 @@
1
+ name: Release Update
2
+ on:
3
+ pull_request:
4
+ types: [closed]
5
+ branches: [main]
6
+ permissions:
7
+ contents: write
8
+ pull-requests: read
9
+ jobs:
10
+ draft:
11
+ if: github.event.pull_request.merged == true
12
+ uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/release-drafter.yml@main
13
+ secrets: inherit
@@ -0,0 +1,21 @@
1
+ name: Publish
2
+ on:
3
+ push:
4
+ tags: ['v*']
5
+ jobs:
6
+ build:
7
+ uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/publish.yml@main
8
+ publish:
9
+ # Inline: PyPI OIDC checks job_workflow_ref, which must point to this file.
10
+ # Moving pypa/gh-action-pypi-publish into cava-devops would break the trusted publisher.
11
+ needs: build
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ id-token: write
15
+ environment:
16
+ name: pypi
17
+ url: https://pypi.org/p/groundskeeping
18
+ steps:
19
+ - uses: actions/download-artifact@v4
20
+ with: { name: dist, path: dist/ }
21
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,228 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ *.lcov
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ # Pipfile.lock
97
+
98
+ # UV
99
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # uv.lock
103
+
104
+ # poetry
105
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
+ # poetry.lock
110
+ # poetry.toml
111
+
112
+ # pdm
113
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
115
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
116
+ # pdm.lock
117
+ # pdm.toml
118
+ .pdm-python
119
+ .pdm-build/
120
+
121
+ # pixi
122
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
123
+ # pixi.lock
124
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
125
+ # in the .venv directory. It is recommended not to include this directory in version control.
126
+ .pixi/*
127
+ !.pixi/config.toml
128
+
129
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
130
+ __pypackages__/
131
+
132
+ # Celery stuff
133
+ celerybeat-schedule*
134
+ celerybeat.pid
135
+
136
+ # Redis
137
+ *.rdb
138
+ *.aof
139
+ *.pid
140
+
141
+ # RabbitMQ
142
+ mnesia/
143
+ rabbitmq/
144
+ rabbitmq-data/
145
+
146
+ # ActiveMQ
147
+ activemq-data/
148
+
149
+ # SageMath parsed files
150
+ *.sage.py
151
+
152
+ # Environments
153
+ .env
154
+ .envrc
155
+ .venv
156
+ env/
157
+ venv/
158
+ ENV/
159
+ env.bak/
160
+ venv.bak/
161
+
162
+ # Spyder project settings
163
+ .spyderproject
164
+ .spyproject
165
+
166
+ # Rope project settings
167
+ .ropeproject
168
+
169
+ # mkdocs documentation
170
+ /site
171
+
172
+ # mypy
173
+ .mypy_cache/
174
+ .dmypy.json
175
+ dmypy.json
176
+
177
+ # Pyre type checker
178
+ .pyre/
179
+
180
+ # pytype static type analyzer
181
+ .pytype/
182
+
183
+ # Cython debug symbols
184
+ cython_debug/
185
+
186
+ # PyCharm
187
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
188
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
189
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
190
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
191
+ # .idea/
192
+
193
+ # Abstra
194
+ # Abstra is an AI-powered process automation framework.
195
+ # Ignore directories containing user credentials, local state, and settings.
196
+ # Learn more at https://abstra.io/docs
197
+ .abstra/
198
+
199
+ # Visual Studio Code
200
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
201
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
202
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
203
+ # you could uncomment the following to ignore the entire vscode folder
204
+ .vscode/
205
+ # Temporary file for partial code execution
206
+ tempCodeRunnerFile.py
207
+
208
+ # Ruff stuff:
209
+ .ruff_cache/
210
+
211
+ # PyPI configuration file
212
+ .pypirc
213
+
214
+ # Marimo
215
+ marimo/_static/
216
+ marimo/_lsp/
217
+ __marimo__/
218
+
219
+ # Streamlit
220
+ .streamlit/secrets.toml
221
+ __pycache__/
222
+ *.py[cod]
223
+ .pytest_cache/
224
+ .mypy_cache/
225
+ .ruff_cache/
226
+ .venv/
227
+ *.ipynb
228
+ _temp/
@@ -0,0 +1,27 @@
1
+ # Contributing
2
+
3
+ ## Development setup
4
+
5
+ ```bash
6
+ uv sync --all-extras --dev
7
+ uv run pytest -q
8
+ uv run ruff check .
9
+ ```
10
+
11
+ ## Opening a pull request
12
+
13
+ 1. Apply **exactly one** label before merging:
14
+
15
+ | Label | When to use |
16
+ |---|---|
17
+ | `breaking` | Public API change, backward-incompatible |
18
+ | `feature` | New functionality, backward-compatible |
19
+ | `fix` | Bug fix |
20
+ | `dependencies` | Dependency version update |
21
+ | `chore` | CI changes, refactoring, test additions, docs — anything that does not affect the public-facing package. Bypasses the label gate; excluded from the changelog and does not bump the version. |
22
+
23
+ 2. When merging (squash), write a clear extended description in the merge dialog. That text — not the PR's opening description — becomes the changelog entry for this change. Leave it blank for `chore` PRs.
24
+
25
+ ## Versioning and releases
26
+
27
+ Versions are derived from git tags; there is no version string in any source file. Releases are triggered by a maintainer publishing the standing draft release on the repository's Releases page. There is no automated commit-back to `main`.
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: groundskeeping
3
+ Version: 0.1.0
4
+ Summary: Reusable Textual operator shell, workbench, configuration browser, and telemetry primitives.
5
+ Author: UNSW CAVA platform tooling
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: oa-configurator<0.3,>=0.1.2
8
+ Requires-Dist: textual<9,>=8.2
9
+ Provides-Extra: dev
10
+ Requires-Dist: mkdocs-material>=9.7.1; extra == 'dev'
11
+ Requires-Dist: mkdocs>=1.6.1; extra == 'dev'
12
+ Requires-Dist: mkdocstrings-python>=2.0.1; extra == 'dev'
13
+ Requires-Dist: pytest>=9.0; extra == 'dev'
14
+ Requires-Dist: ruff>=0.4; extra == 'dev'
15
+ Requires-Dist: ty>=0.0.59; extra == 'dev'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # groundskeeping
19
+
20
+ `groundskeeping` is a reusable Textual shell for operator tools that help people care for a working environment: setup checks, configuration, queues, telemetry, tuning, and long-running operations.
21
+
22
+ The package provides the shared operating frame. A consumer application provides the domain. Keep that distinction close and most design decisions become simpler.
23
+
24
+ ## The Shape Of An App
25
+
26
+ A `groundskeeping` app starts with one `OperatorAppSpec`.
27
+
28
+ The spec names the app, orders the pages, registers the actions, and supplies the policies that decide whether work may run. It is the composition root: everything application-specific should arrive there from the consumer, not through global lookup inside the shared package.
29
+
30
+ Pages are owned by the consumer and are ordinary Textual widgets. The shell mounts them once, activates and deactivates them as the operator moves between tabs, and preserves page-local state. A page receives a narrow `PageContext`; it does not receive the whole app.
31
+
32
+ The default page surface is the workbench:
33
+
34
+ - catalogue on the left;
35
+ - rows or tree content on the upper right; and
36
+ - selected detail on the lower right.
37
+
38
+ Pages render package-owned view models such as `CatalogueItem`, `TableView`, `TreeView`, `EmptyView`, `TextView`, and `KeyValueView`. Domain objects should be translated before they reach the workbench. That keeps the shared shell reusable and keeps consumer language in the consumer.
39
+
40
+ ## Setup Pages
41
+
42
+ A setup page should answer a concrete operator question: "can this environment do the work I am about to ask of it?"
43
+
44
+ The page should normally live in the consumer package and use consumer services to inspect the environment. `groundskeeping` supplies the rendering and action contracts; it should not know what "ready" means for Groundworkers, CAVA, or any future application.
45
+
46
+ A good setup page usually has:
47
+
48
+ - a catalogue of setup areas, such as config, database, runtime, model server, paths, or credentials;
49
+ - a landing `TreeView` summarising overall readiness;
50
+ - a `TableView` for repeated checks where scanning matters;
51
+ - `KeyValueView` detail for the selected check;
52
+ - one or two safe verification actions; and
53
+ - an operation policy that describes effects in the consumer's own vocabulary.
54
+
55
+ Start read-only. Verification actions are a good first step because they exercise the shell, action contracts, progress reporting, and failure presentation without taking ownership of durable setup changes too early.
56
+
57
+ ## Actions And Jobs
58
+
59
+ Actions are declarations plus runners.
60
+
61
+ An `ActionSpec` describes the operator-facing command: label, summary, fields, resources, effects, cancellation mode, and runner. `FieldSpec` parses and redacts input before it reaches the runner. The runner receives an `ActionContext` with progress and cancellation, so long-running work can report what it is doing without importing widgets.
62
+
63
+ A shell job is work launched by this TUI process. It is not a durable processing queue. Use `JobManager` and `JobPolicy` to gate in-process work and show progress; keep queue state, retries, leases, and durable records inside the consumer.
64
+
65
+ ## Configuration
66
+
67
+ `groundskeeping.configurator` understands the public shape of `oa-configurator` stack configuration well enough to inspect and present it safely. It can build snapshots, section views, drafts, redacted diffs, and apply intents.
68
+
69
+ It does not write TOML. Persistence belongs to the public `oa-configurator` mutation API and to the consumer's operation policy. That separation protects comments, secrets, external edits, and tenant-specific safety rules.
70
+
71
+ Consumer applications can add `ConfigResourceAdapter` implementations for resource types that need better labels, choices, validation, verification, or post-apply effects.
72
+
73
+ ## Telemetry
74
+
75
+ Telemetry has a headless core and Textual widgets layered above it.
76
+
77
+ `groundskeeping.telemetry` contains source protocols, availability, normalized metrics, snapshots, and sampling runtime. It must remain free of Textual imports so collectors can be tested and reused outside a running app.
78
+
79
+ `groundskeeping.widgets.telemetry` renders snapshots. Widgets should bind to metric keys and capabilities, not concrete provider classes. A GPU card, for example, should care about accelerator utilisation and memory metrics; it should not need to know whether the source is NVIDIA, Apple Silicon, or something added later.
80
+
81
+ Consumers own domain telemetry: queue depth, pipeline progress, database state, workload throughput, and tuning interpretation.
82
+
83
+ ## Ownership Boundary
84
+
85
+ `groundskeeping` owns:
86
+
87
+ - route validation and page activation;
88
+ - mounted-page state preservation;
89
+ - the shared workbench surface;
90
+ - generic view models;
91
+ - action, field, progress, cancellation, and job contracts;
92
+ - in-process job gating;
93
+ - read-only configuration inspection and draft/diff models;
94
+ - Textual-free infrastructure telemetry contracts; and
95
+ - reusable widgets that render normalized models.
96
+
97
+ Consumers own:
98
+
99
+ - every production page;
100
+ - domain presenters, controllers, and services;
101
+ - queue semantics and durable records;
102
+ - YAML or other consumer configuration formats;
103
+ - resource-specific `oa-configurator` adapters;
104
+ - model calls, database access, and runtime execution;
105
+ - domain telemetry and tuning algorithms;
106
+ - operation safety policy; and
107
+ - application branding and help text.
108
+
109
+ ## Commenting Style
110
+
111
+ Write comments for the next person adapting the tool.
112
+
113
+ Good comments explain why a boundary exists, what operator-facing behaviour depends on it, and where domain logic should stay. They are especially useful around Textual lifecycle methods, event routing, worker handoffs, cancellation, secret redaction, and extension points.
114
+
115
+ Prefer comments that preserve intent over comments that narrate syntax. Explain why row events return to the active page through the workbench surface. Do not explain that a loop iterates over rows.
116
+
117
+ If a future maintainer is likely to wonder "why is this shaped this way?", leave them a small signpost.
118
+
119
+ ## Running The Demo
120
+
121
+ ```bash
122
+ uv run groundskeeping
123
+ ```
124
+
125
+ The demo composes overview, configuration, and telemetry pages. It also registers a small action so the app spec and action registry can be exercised without a consumer application.
126
+
127
+ ## Running Tests
128
+
129
+ ```bash
130
+ uv run --extra dev pytest -q
131
+ ```
132
+
133
+ The tests cover route validation, app startup, action and job contracts, configurator redaction, telemetry import boundaries, and consumer dependency boundaries.