Skip to content

Commit 4433c59

Browse files
committed
feat(rebrand): ChoreOps rebrand progress
What changed: Migrated dashboard namespace from kcd- to cod- with legacy compatibility handling for existing URLs and discovery. Rebranded user-facing copy across translations, notifications, and dashboard templates; updated functional template domain calls to choreops. Finalized Phase 2 utility/reference updates and Phase 3 cleanup (agent docs + CI brand-regression guard). Hardened workflows for Phase 5: added Crowdin secret preflight/skip logic and clearer HACS prerequisite guidance. Updated in-process rebrand plan progress and blockers based on workflow validation findings. Why it matters: Completes the public/runtime rebrand path while protecting existing installs during transition. Reduces false CI failures and makes external dependency blockers explicit. Brings release readiness forward with validated lint/type/boundary checks and full pytest pass via python -m pytest.
1 parent a683a6b commit 4433c59

20 files changed

Lines changed: 425 additions & 346 deletions

.github/agents/ChoreOps Builder.agent.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ Why it matters:
230230
- **Ask if unsure**: Don't waste time comparing - just ask user for scope confirmation
231231
| Logging | `LOGGER.debug("val: %s", var)` — no f-strings |
232232
| Types | 100% hints, `str \| None` not `Optional[str]` |
233-
| Storage | `.storage/kidschores_data`, never `config_entry.data` |
233+
| Storage | `.storage/choreops/choreops_data`, never `config_entry.data` |
234234

235235
## Validation Gates (Required)
236236

.github/agents/ChoreOps Strategist.agent.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ Before delivering plan:
166166

167167
### Schema Changes
168168

169-
If plan affects `.storage/kidschores_data`:
169+
If plan affects `.storage/choreops/choreops_data`:
170170

171171
1. Note SCHEMA_VERSION increment in Phase 1
172172
2. Add migration step: `_migrate_to_v{VERSION}()`

.github/workflows/lint-validation.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ jobs:
3737
pip install ruff mypy pytest pytest-asyncio pytest-homeassistant-custom-component
3838
pip install types-python-dateutil types-PyYAML
3939
40+
- name: Brand regression guard
41+
run: |
42+
set -euo pipefail
43+
if grep -RInE "ad-ha/kidschores-ha|kidschores-ha-dashboard" \
44+
.github README.md pyproject.toml hacs.json custom_components/choreops/manifest.json; then
45+
echo "❌ Deprecated KidsChores branding string found in active project files"
46+
exit 1
47+
fi
48+
4049
- name: Run Quick Lint
4150
run: |
4251
chmod +x ./utils/quick_lint.sh

.github/workflows/translation-sync-manual.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,24 @@ jobs:
4040
with:
4141
ref: ${{ inputs.branch || github.ref_name }}
4242

43+
- name: Validate Crowdin configuration
44+
id: crowdin_preflight
45+
env:
46+
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
47+
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
48+
run: |
49+
if [ -z "$CROWDIN_PROJECT_ID" ] || [ -z "$CROWDIN_PERSONAL_TOKEN" ]; then
50+
echo "run_crowdin=false" >> "$GITHUB_OUTPUT"
51+
echo "::warning::Crowdin secrets are not configured. Skipping translation sync."
52+
echo "::notice::Set CROWDIN_PROJECT_ID and CROWDIN_PERSONAL_TOKEN repository secrets to enable this workflow."
53+
else
54+
echo "run_crowdin=true" >> "$GITHUB_OUTPUT"
55+
echo "::notice::Crowdin secrets detected. Translation sync will run."
56+
fi
57+
4358
# STEP 1: Upload Sources
4459
- name: Upload Sources
60+
if: steps.crowdin_preflight.outputs.run_crowdin == 'true'
4561
uses: crowdin/github-action@v2
4662
with:
4763
upload_sources: true
@@ -56,6 +72,7 @@ jobs:
5672
# STEP 2: Trigger Google Translate (MT) via Crowdin CLI
5773
# This auto translate service has a cost associated with it, so it may be stopped or disabled in the future.
5874
- name: Trigger Google Translate
75+
if: steps.crowdin_preflight.outputs.run_crowdin == 'true'
5976
run: |
6077
npm install -g @crowdin/cli
6178
crowdin pre-translate \
@@ -68,6 +85,7 @@ jobs:
6885

6986
# STEP 3: Download & Create PR
7087
- name: Download Translations
88+
if: steps.crowdin_preflight.outputs.run_crowdin == 'true'
7189
uses: crowdin/github-action@v2
7290
with:
7391
upload_sources: false
@@ -81,3 +99,8 @@ jobs:
8199
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82100
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
83101
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
102+
103+
- name: Crowdin sync skipped
104+
if: steps.crowdin_preflight.outputs.run_crowdin != 'true'
105+
run: |
106+
echo "Crowdin sync skipped: repository secrets are not configured yet."

.github/workflows/validate.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ jobs:
1414
if: github.repository == 'ccpk1/choreops' && (github.ref == 'refs/heads/main' || github.event_name == 'pull_request')
1515
steps:
1616
- uses: "actions/checkout@v3"
17+
- name: HACS prerequisite guidance
18+
run: |
19+
echo "::notice::HACS validation requires a valid integration manifest at custom_components/choreops/manifest.json and a valid hacs.json file."
20+
echo "::notice::The brands check also requires this integration to be registered in the Home Assistant brands repository custom domain catalog."
21+
echo "::notice::If brands registration is pending, HACS may fail even when local lint/tests pass."
1722
- name: HACS validation
1823
uses: "hacs/action@main"
1924
with:

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,20 @@ Strip away the game layer entirely. Use the **Enterprise-Grade Scheduling** and
3939

4040
**The best of both worlds.** Configure some profiles with full gamification to drive engagement, while keeping other profiles strictly utilitarian.
4141

42-
---
43-
4442
<p align="center">
4543
<b>No subscription. No cloud lock-in. 100% local on your Home Assistant instance.</b>
4644
</p>
4745

46+
---
47+
4848
> **Attribution & Legacy**<br>
4949
> ChoreOps is the official evolution of the **KidsChores** integration. While the original project is now deprecated, its concepts and features grew into this new system to better serve the entire Home Assistant community—expanding the scope beyond just "kids" to the whole household.
5050
>
5151
> The original creator, **@ad-ha**, remains involved with this progression and continues to inspire the project's direction.<br>
5252
> 🔄 **Coming from KidsChores?** We have a direct migration path to move your data over. **[View the Migration Guide →](https://github.com/ccpk1/choreops/wiki/Migration)**
5353
54+
---
55+
5456
## Key capabilities
5557

5658
- 🧠 **Intelligent logic**: sophisticated recurring schedules, first-come-first-served pools, per-kid schedules, and complex rotation algorithms

custom_components/choreops/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: KidsChoresConfigEntry) -
226226
)
227227

228228
# Dashboard storage dedupe (v0.5.0 migration safety)
229-
# Remove stale duplicate kcd-* records to prevent Lovelace panel collisions
229+
# Remove stale duplicate cod-/kcd- records to prevent Lovelace panel collisions
230230
# on subsequent Home Assistant startups.
231231
try:
232232
from .helpers import dashboard_builder as dbuilder

custom_components/choreops/config_flow.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,9 @@ async def _handle_use_current(self):
246246
if not result.get("prepared"):
247247
error_key = result.get("error")
248248
if error_key == "file_not_found":
249-
return self.async_abort(reason=const.TRANS_KEY_CFOP_ERROR_FILE_NOT_FOUND)
249+
return self.async_abort(
250+
reason=const.TRANS_KEY_CFOP_ERROR_FILE_NOT_FOUND
251+
)
250252
if error_key == "corrupt_file":
251253
return self.async_abort(reason=const.TRANS_KEY_CFOP_ERROR_CORRUPT_FILE)
252254
if error_key == "invalid_structure":
@@ -465,10 +467,14 @@ async def async_step_paste_json_input(
465467
}
466468

467469
# Write to storage file
468-
storage_path = Path(KidsChoresStore(self.hass).get_storage_path())
470+
storage_path = Path(
471+
KidsChoresStore(self.hass).get_storage_path()
472+
)
469473

470474
await self.hass.async_add_executor_job(
471-
lambda: storage_path.parent.mkdir(parents=True, exist_ok=True)
475+
lambda: storage_path.parent.mkdir(
476+
parents=True, exist_ok=True
477+
)
472478
)
473479

474480
# Write wrapped data to storage (directory created by HA/test fixtures)

custom_components/choreops/const.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,10 @@ def set_default_timezone(hass):
114114
# Schema version for template context structure (bump when context dict changes)
115115
DASHBOARD_TEMPLATE_SCHEMA_VERSION: Final = 1
116116

117-
# URL path prefix for generated dashboards (e.g., kcd-alice, kcd-admin)
118-
DASHBOARD_URL_PATH_PREFIX: Final = "kcd-"
117+
# URL path prefix for generated dashboards (e.g., cod-alice, cod-admin)
118+
DASHBOARD_URL_PATH_PREFIX: Final = "cod-"
119+
# Legacy URL prefix retained during compatibility window
120+
DASHBOARD_LEGACY_URL_PATH_PREFIX: Final = "kcd-"
119121

120122
# Available dashboard styles
121123
DASHBOARD_STYLE_FULL: Final = "full"
@@ -141,13 +143,15 @@ def set_default_timezone(hass):
141143

142144
# Supported release-tag grammar (parser contract)
143145
# Accepted examples:
146+
# - COD_v0.5.0_beta3
147+
# - COD_v0.5.0-beta3
144148
# - KCD_v0.5.0_beta3
145149
# - KCD_v0.5.0-beta3
146150
# - v0.5.0-beta3
147151
# - v0.5.4
148152
# - 0.5.4
149153
DASHBOARD_RELEASE_TAG_PATTERN: Final = (
150-
r"^(?:(?:KCD_)?v)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)"
154+
r"^(?:(?:KCD_|COD_)?v)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)"
151155
r"(?:(?:_|-)?(?P<pre_label>beta|b)(?P<pre_num>\d+))?$"
152156
)
153157

custom_components/choreops/helpers/dashboard_builder.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ class DashboardReleaseTag:
6868
"""Normalized representation of a supported dashboard release tag.
6969
7070
Supported formats:
71-
- `KCD_vX.Y.Z` / `vX.Y.Z` / `X.Y.Z`
72-
- `KCD_vX.Y.Z_betaN` / `KCD_vX.Y.Z-betaN` / `vX.Y.Z-betaN`
71+
- `COD_vX.Y.Z` / `KCD_vX.Y.Z` / `vX.Y.Z` / `X.Y.Z`
72+
- `COD_vX.Y.Z_betaN` / `COD_vX.Y.Z-betaN` / `vX.Y.Z-betaN`
7373
"""
7474

7575
raw_tag: str
@@ -596,10 +596,20 @@ def get_multi_view_url_path(dashboard_name: str) -> str:
596596
dashboard_name: User-specified dashboard name (e.g., "Chores").
597597
598598
Returns:
599-
Slugified URL path with kcd- prefix (e.g., "kcd-chores").
599+
Slugified URL path with cod- prefix (e.g., "cod-chores").
600600
"""
601601
slug = slugify(dashboard_name.lower())
602-
return f"kcd-{slug}"
602+
return f"{const.DASHBOARD_URL_PATH_PREFIX}{slug}"
603+
604+
605+
def _is_choreops_dashboard_url_path(url_path: str) -> bool:
606+
"""Return True for current or legacy ChoreOps dashboard URL paths."""
607+
return url_path.startswith(
608+
(
609+
const.DASHBOARD_URL_PATH_PREFIX,
610+
const.DASHBOARD_LEGACY_URL_PATH_PREFIX,
611+
)
612+
)
603613

604614

605615
def _get_collection_items(collection: DashboardsCollection) -> list[dict[str, Any]]:
@@ -623,7 +633,7 @@ def check_dashboard_exists(hass: HomeAssistant, url_path: str) -> bool:
623633
624634
Args:
625635
hass: Home Assistant instance.
626-
url_path: Dashboard URL path to check (e.g., "kcd-alice").
636+
url_path: Dashboard URL path to check (e.g., "cod-alice").
627637
628638
Returns:
629639
True if dashboard exists, False otherwise.
@@ -648,7 +658,7 @@ async def async_check_dashboard_exists(hass: HomeAssistant, url_path: str) -> bo
648658
649659
Args:
650660
hass: Home Assistant instance.
651-
url_path: Dashboard URL path to check (e.g., "kcd-alice").
661+
url_path: Dashboard URL path to check (e.g., "cod-alice").
652662
653663
Returns:
654664
True if dashboard exists, False otherwise.
@@ -689,7 +699,7 @@ async def async_dedupe_kidschores_dashboards(
689699
item_url_path = item.get(CONF_URL_PATH)
690700
if not isinstance(item_url_path, str):
691701
continue
692-
if not item_url_path.startswith(const.DASHBOARD_URL_PATH_PREFIX):
702+
if not _is_choreops_dashboard_url_path(item_url_path):
693703
continue
694704
if url_path and item_url_path != url_path:
695705
continue
@@ -772,7 +782,7 @@ async def create_kidschores_dashboard(
772782
icon: Dashboard icon.
773783
774784
Returns:
775-
The URL path of the created dashboard (e.g., "kcd-chores").
785+
The URL path of the created dashboard (e.g., "cod-chores").
776786
777787
Raises:
778788
DashboardExistsError: If dashboard exists and force_rebuild is False.
@@ -1282,15 +1292,15 @@ async def delete_kidschores_dashboard(
12821292
12831293
Args:
12841294
hass: Home Assistant instance.
1285-
url_path: Dashboard URL path (e.g., "kcd-alice").
1295+
url_path: Dashboard URL path (e.g., "cod-alice").
12861296
12871297
Raises:
12881298
DashboardSaveError: If deletion fails.
12891299
"""
12901300
if hass.config.recovery_mode:
12911301
raise DashboardSaveError("Cannot delete dashboards in recovery mode")
12921302

1293-
if not url_path.startswith("kcd-"):
1303+
if not _is_choreops_dashboard_url_path(url_path):
12941304
raise DashboardSaveError(f"Cannot delete non-KidsChores dashboard: {url_path}")
12951305

12961306
await _delete_dashboard(hass, url_path)

0 commit comments

Comments
 (0)