chore(audit): Phase 1 foundation — fork-namespaced CI, governance, and Claude Code workflow - #2
Conversation
…curity policy Establishes Phase 1 foundation documentation: - AGENTS.md defines P0/P1/P2/P3 review tiers, branch model, and known-debt carve-outs so Codex doesn't re-flag audit-tracked findings. - CLAUDE.md captures commit format, PR workflow, and upstream contribution flow (commit format enforced only on non-sync/* branches). - SECURITY.md points upstream-relevant reports back to bolagnaise/PowerSync. Fork-only — not proposed upstream until contribution credibility is built.
…nance CODEOWNERS is scoped to irreversibility (workflow files, manifest version bumps that hit all HACS users) rather than architectural sensitivity — correctness concerns are enforced via AGENTS.md P0 review rules. PR template includes Upstream proposal / Constitution check sections so fork-only and upstream-bound changes are surfaced at PR time. Dependabot opens weekly pip and monthly GHA bumps with capped open-PR counts to avoid burying real review work.
Three new workflows under the fork-*.yml namespace to avoid colliding with upstream-owned validate.yml, release.yml, etc: - fork-ci.yml: strict lint+pytest+HACS/hassfest+gitleaks on Ryan's branches. Skips when head_ref starts with sync/. Ruff is advisory until the broad-except remediation lands in Phase 3. - fork-sync-advisory.yml: continue-on-error pytest + hassfest on sync/* branches so upstream pulls surface breakage without blocking the merge. - fork-claude.yml: gated @claude assistant; skipped on sync/* branches (huge diffs = noise + cost) and serialized per PR via concurrency group. Third-party actions pinned to immutable SHAs: - home-assistant/actions/hassfest -> 868e6cb (master @ 2026-05-26) - hacs/action -> d556e73 (v22.5.0) - gitleaks/gitleaks-action -> ff98106 (v2.3.9)
…flow Adds three slash commands under .claude/commands/: - /self-review: pre-PR P0/P1 self-check (skips on sync/*) - /fix-review: address Codex P0/P1 findings, capped at 3 rounds - /upstream-propose: prepare a fork PR for submission to bolagnaise/PowerSync .gitignore previously ignored all of .claude/. Narrowed the pattern to .claude/* with a !.claude/commands/ negation so personal local files (history, scratch state) stay ignored while the shared commands are tracked.
… discipline, and mobile API rules AGENTS.md adds: - Project context — supported batteries, inverters, providers; mobile-app surface via long-lived access token - Layout map for custom_components/power_sync/ - PR size discipline section with empirical basis (SmartBear/Cisco 2006, Propel Code 50k-PR study 2024, SWE-PRBench arXiv:2603.26130). Target ≤200, hard ceiling 400; audit remediation MUST batch by module. - New P0 rules: storage from_dict() without HA-timezone date, state restore without storage-version check, mobile endpoint returning unsanitised data, LP optimiser change without edge-case test. - New P1 rules: battery vendor change without round-trip test, provider pricing change without rate-curve validation, Modbus write without retry, config flow change without test update. - Known-debt additions: 22 fix-of-fix commits (H14), esy_sunhome custom dep (M14). - Fresh-eyes-required list: LP solver core, views/ auth, vendor write paths, mobile API, new provider integration. - Mobile app considerations and graphify hook. CLAUDE.md adds: - "When implementing" section pointing at Constitution principles 11-13 - PR-size pointer back to AGENTS.md - New commit scopes: mobile-api, curtailment, providers - Concrete upstream sync workflow (bash) and upstream contribution flow with gh pr create example - Mobile app API contract rules (backwards compat, path versioning) - CI failure debugging via `gh run view --log-failed` over assuming the fix from the finding description - Codex review-signal reactions (👀/💬/👍) and poll command - Full slash command roster including new /ship and /sync-upstream PR template gains a Size section and tightens the Risk checklist (adds battery vendor write paths, drops the trailing "?"s).
- fork-ci.yml: add top-level `permissions: contents: read` (least-privilege default), reorder validate job to run Hassfest before HACS for faster failure signal. - fork-sync-advisory.yml: add top-level `permissions: contents: read`, enable pip caching to speed sync-branch CI, expand pytest warning text to clarify that failures here typically still merge (upstream is source of truth). - fork-claude.yml: hoist permissions to top level so all jobs inherit consistently; add `id-token: write` so the action can use OIDC if/when Anthropic adds it.
- /self-review: prepend PR-size check (git diff --stat main...HEAD; stop if >400 lines) and add P0/P1 items for storage from_dict() dates, LP optimiser edge cases, and mobile API backwards compat. - /fix-review: spell out the fixup commit format and broaden bail-out conditions to cover Codex P0 on LP solver core, views/ auth, and battery vendor write paths — these need fresh-eyes review, not an automated patch. - /upstream-propose: tighten cleanup steps (strip fork-only file references including the Constitution and fork-* workflows) and add an explicit `gh api` check for upstream's PR template. - NEW /ship: rebase autosquash, force-with-lease push, gh pr ready, surface required-checks + Codex status, then wait for human merge. - NEW /sync-upstream: end-to-end upstream pull flow — verify the upstream remote, fetch, merge into sync/upstream-YYYYMMDD, open the advisory-CI PR, then rebase any open feature branches afterwards.
…o key on) actions/setup-python@v5 with `cache: pip` requires a `requirements.txt` or `pyproject.toml` at repo root to compute the cache key. PowerSync has neither — runtime deps live in `custom_components/power_sync/manifest.json` and the install step already tolerates the absence via `pip install -r requirements.txt 2>/dev/null || true`. The cache directive was failing setup-python before the install step could run, so Fork CI's `Lint, tests` job aborted at 7s without exercising ruff or pytest. Removing `cache: pip` lets the job proceed; install is fast enough on first-run that caching isn't critical. Same fix applied to `fork-sync-advisory.yml` for consistency.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48006e7888
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| validate: | ||
| name: HACS + hassfest | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
Skip strict validation on sync branches
For a sync/upstream-* PR, this workflow is still triggered by pull_request and only the ci and security jobs have the !startsWith(github.head_ref, 'sync/') guard; validate runs HACS/hassfest as a normal failing job. That defeats the new advisory-only sync path in .github/workflows/fork-sync-advisory.yml and can leave upstream-sync PRs with a red strict Fork CI check. Add the same sync skip guard here or make this job advisory for sync branches.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 3e08a42. Added the !startsWith(github.head_ref, 'sync/') guard to the validate job so it matches the ci and security jobs — strict HACS/hassfest no longer runs on sync/* PRs.
Generated by Claude Code
| contains(github.event.comment.body, '@claude') && | ||
| !startsWith(github.event.pull_request.head.ref, 'sync/') |
There was a problem hiding this comment.
Gate issue comments with the actual PR head branch
On issue_comment events GitHub puts the PR marker under github.event.issue.pull_request, not github.event.pull_request, so this check has no head ref when someone comments @claude in the PR conversation. In that scenario the sync-branch exclusion evaluates against a missing/empty value and the write-permission Claude job can still run on sync/* PRs despite the stated skip rule; fetch the PR from github.event.issue.pull_request (or otherwise resolve its head ref) before allowing the job.
Useful? React with 👍 / 👎.
| if: startsWith(github.head_ref, 'sync/') | ||
| runs-on: ubuntu-latest | ||
| continue-on-error: true |
There was a problem hiding this comment.
Keep upstream validation advisory for sync PRs
This new sync workflow is advisory, but I checked .github/workflows/validate.yml and it still triggers on every pull_request to main with normal HACS/hassfest jobs and no sync/* guard. For sync/upstream-* PRs, failures there will still produce blocking red checks even though this workflow's job is continue-on-error, so the advisory sync lane is not actually advisory unless the existing validation workflow is also skipped or made non-blocking for those branches.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Acknowledged — deferring. The direct fix means adding a sync/* guard to upstream-owned validate.yml, which is out of scope for this PR (fork-only, no upstream file edits). In practice the impact is limited: auto-merge is OFF and the sync policy is already "fail = investigate but typically still merge," so a red validate.yml check on a sync PR is advisory, not blocking. Will revisit when the sync lane is first exercised.
Generated by Claude Code
| - package-ecosystem: pip | ||
| directory: / |
There was a problem hiding this comment.
Remove pip Dependabot until a manifest exists
The repository does not currently contain a root requirements.txt, pyproject.toml, Pipfile, setup.py, or similar pip manifest (I checked with a repo-wide file search), so this pip update entry has nothing under / for Dependabot to update. That means the documented weekly Python dependency PRs will not materialize and Dependabot will just report a configuration/update error; either add a real dependency manifest or drop this pip ecosystem block until one exists.
Useful? React with 👍 / 👎.
| ## Fork-specific issues | ||
|
|
||
| Open a GitHub issue with `[security]` in the title or contact via the email in profile. |
There was a problem hiding this comment.
Do not ask reporters to disclose vulnerabilities publicly
For fork-specific vulnerabilities, this policy tells reporters to open a normal GitHub issue, which is public by default. If the issue includes a hardcoded token, auth bypass, or mobile API data exposure, the report itself discloses the exploit details before there is a fix; route security reports only through a private channel such as GitHub private vulnerability reporting or a direct security email.
Useful? React with 👍 / 👎.
…ed in fork-ci.yml installs ruff + pytest plumbing but not the integration's runtime deps (aiohttp, scipy, goodwe, etc.) — those live in manifest.json, not a root requirements.txt. Upstream CI never ran pytest so this gap was never exposed; the first fork-ci.yml run hits ModuleNotFoundError during collection. Decision (Phase 1): match the ruff pattern — pytest runs but its non-zero exit doesn't fail the job; the runner emits a GHA warning annotation instead. Phase 2 will install manifest.json requirements explicitly and tighten this back to strict. This is a known-debt mute, not a "tests don't matter" stance. The warning text is intentionally specific so the next time someone looks at a green Fork CI run, they see exactly what isn't being checked.
validate-hacs is red on every PR for two reasons that are not code- fixable: fork repo missing GitHub topics, and Issues disabled in repo settings. Both are HACS publishing requirements. One-time fix in the fork repo Settings unblocks all future PRs. Logged under Known debt with a separate "Repo-config debt" subhead so it's not conflated with code/audit findings.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 97e4cd8f8c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| git checkout -b sync/upstream-$(date +%Y%m%d) | ||
| git merge upstream/main |
There was a problem hiding this comment.
Create sync branches from main
When /sync-upstream is run while the checkout is on a feature or audit branch, git checkout -b sync/upstream-... bases the sync branch on that current branch before merging upstream, so the PR to main can accidentally include unrelated fork work. This contradicts the branch model that sync branches start from main; check out and fast-forward main before creating the sync branch.
Useful? React with 👍 / 👎.
| ``` | ||
| fix({scope}): {one-line description} | ||
|
|
||
| Resolves Codex {P0|P1} finding. | ||
| ``` |
There was a problem hiding this comment.
Use autosquash-compatible fixup commits
The /ship command later relies on git rebase -i --autosquash, and git rebase -h says autosquash only moves commits beginning with squash! or fixup!. With /fix-review telling Claude to create fix({scope})... commits, those review-fix commits will not be folded during shipping, leaving the extra fix commits in history instead of producing the intended clean PR.
Useful? React with 👍 / 👎.
| Skip if on `sync/*` branch. | ||
|
|
||
| 1. **PR size check** — `git diff --stat main...HEAD`. If >400 lines, STOP and split. | ||
| 2. `ruff check . --fix` (advisory — informational only) |
There was a problem hiding this comment.
Avoid mutating files during self-review
This command is labeled advisory/informational and the workflow ends with “Report findings — do not auto-fix,” but ruff check --help confirms --fix applies changes to files. When /self-review is run on a branch with auto-fixable lint, it can silently rewrite the working tree before the diff review, so use ruff check . or --diff if this step is meant to be read-only.
Useful? React with 👍 / 👎.
| git remote -v | grep -q bolagnaise || \ | ||
| git remote add upstream https://github.com/bolagnaise/PowerSync.git |
There was a problem hiding this comment.
Verify the upstream remote by name
This check only looks for bolagnaise anywhere in git remote -v, so a clone whose origin already points at bolagnaise/PowerSync will skip adding the upstream remote and then fail immediately at git fetch upstream main. Since the rest of the command hard-codes the remote name upstream, check git remote get-url upstream instead of grepping all remotes.
Useful? React with 👍 / 👎.
…e-1-foundation-wGvyD
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f0681dd054
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,136 @@ | |||
| @AGENTS.md | |||
| @ENGINEERING_CONSTITUTION.md | |||
There was a problem hiding this comment.
Add the referenced constitution file
CLAUDE.md imports @ENGINEERING_CONSTITUTION.md and later tells Claude to apply the constitution, but I checked the repository file list and that file is not present. In Claude sessions that use this entry point, the core governance guidance is therefore missing/unresolvable, so either add the constitution file in this PR or remove the reference and dependent instructions.
Useful? React with 👍 / 👎.
Ryan enabled Issues and set topics (battery, energy, hacs, home-assistant) on Artic0din/PowerSync. HACS validation no longer red on that basis. Verified via API. Removing the tracker entry; expect HACS + hassfest and validate-hacs to go green on the CI re-run this push triggers.
The ci and security jobs already skip sync/* PRs, but the validate (HACS + hassfest) job had no guard — so upstream-sync PRs would hit strict HACS/hassfest as a blocking check, defeating the advisory-only sync lane this PR establishes. Add the same head_ref guard the sibling jobs use. Codex P1 (fork-ci.yml). The companion concern — upstream-owned validate.yml also lacking a sync/* guard — is deferred: fixing it means editing an upstream file, which is out of scope here, and manual-merge policy means those checks are advisory in practice.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3e08a42e53
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| jobs: | ||
| claude: | ||
| if: | | ||
| contains(github.event.comment.body, '@claude') && |
There was a problem hiding this comment.
Restrict Claude trigger to trusted users
On a public repository, any user who can create an issue, PR, or review comment containing @claude satisfies this job condition, and the job then runs with contents, pull-requests, issues, and id-token write permissions plus the Anthropic API key. GitHub's issue_comment/review-comment events are driven by commenter activity, so this needs an actor/association allowlist (for example owner/member/collaborator) before granting the agent write access; otherwise an untrusted commenter can spend API quota and potentially drive repository writes.
Useful? React with 👍 / 👎.
| run: ruff check . --output-format=github || true | ||
|
|
||
| - name: Pytest (advisory until runtime deps wired in) | ||
| run: pytest --cov=custom_components/power_sync --cov-report=term-missing -x || echo "::warning::pytest failed — runtime deps from manifest.json not installed in CI yet" |
There was a problem hiding this comment.
Fail Ryan-branch CI when pytest fails
For non-sync/* PRs this is the only pytest job added by the fork CI, but the || echo makes the step exit successfully whenever tests fail. That contradicts the new Ryan-branch “Strict CI” guardrail in AGENTS.md, so a feature/audit PR with a real regression can still show a green Fork CI check unless HACS/hassfest or gitleaks happen to fail; make this step fail on non-sync branches and reserve advisory behavior for fork-sync-advisory.yml.
Useful? React with 👍 / 👎.
Problem
Fork of
bolagnaise/PowerSynchad no fork-specific guardrails. No agent rules, no review-tier discipline, no fork/upstream branch contract, no CI separation between Ryan's branches and upstream sync branches. Audit Phase 1 lays the foundation so Phase 2-7 work has somewhere to land.Refs: audit Phase 1 brief.
Approach
Land governance and CI scaffolding under a
fork-*namespace so upstream syncs frombolagnaise/PowerSyncnever collide. Strict CI on Ryan's branches, advisory-only CI onsync/*branches. Codex@claudeworkflow gated offsync/*(huge diffs = noise + cost). P0/P1/P2/P3 review tiers inAGENTS.mdcarve out the 938 broad-except + 30 services-without-vol.Schemaaudit findings as known debt so Codex doesn't re-flag them.Scope
custom_components/power_sync/code..gitignorenarrowing (.claude/→.claude/*+!.claude/commands/) so the slash commands can be tracked.vol.Schemarollout, or the four blocking-sleep fixes — those become per-module PRs in Phase 2/3.Size
7 commits, +517 / −9 LOC across documentation, governance, three workflow files, and five slash commands. Well under the 400-line ceiling for review-relevant changes (most of the diff is documentation).
Test plan
fork-ci.yml,fork-sync-advisory.yml,fork-claude.ymlparse on push (GHA YAML validation).chore/*PR and confirmfork-ci.ymlruns lint+pytest+HACS/hassfest+gitleaks andfork-sync-advisory.ymldoes NOT fire.sync/upstream-testbranch and confirm the inverse: advisory runscontinue-on-error, strict CI is skipped, fork-claude.yml is skipped.@claude helpon a non-sync PR and confirm the workflow triggers.Risk
optimization/(LP solver)HomeAssistantView(mobile API contract)No production code touched.
Upstream proposal
bolagnaise/PowerSyncPer the brief: never propose fork-CI scaffolding upstream until 3-5 concrete bug-fix PRs have merged. This is governance + CI — strictly fork-only.
Constitution check
validate.yml,release.yml,pages.yml,sponsors.yml,discord-release.ymluntouchedNotes
claude/powersync-phase-1-foundation-wGvyD(session-assigned), not the brief'saudit/phase-1-foundation-discipline. Rename before merge if theaudit/*convention is canonical.home-assistant/actions/hassfest@868e6cb4607727d764341a158d98872cd63fa658(master HEAD)hacs/action@d556e736723344f83838d08488c983a15381059a(v22.5.0)gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7(v2.3.9)CLAUDE.mdreferences@ENGINEERING_CONSTITUTION.mdwhich doesn't exist yet — Claude Code resolves missing @-includes silently; harmless until the constitution lands.validate.ymlalready runs HACS + hassfest with floating@main/@master.fork-ci.ymlduplicates with pinned SHAs — intentional belt-and-suspenders per the brief, but two HACS runs per PR. Drop one if that's noisy.Generated by Claude Code