Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Summary

<!-- What changed and why (one short paragraph). -->

## Traceability

- **Task ID(s):** T0xx
- **Traces:** AC-… / FR-… / NFR-… (from `tasks.md` `**Traces**:` fields)
- **PRD silent?** No / Yes — proposed PRD change: …

## Proof

- [ ] Gate 2 locally: `bash scripts/check-traceability.sh` (or rely on CI `traceability` job)
- [ ] Unit tests / AC-named tests updated or justified as tracked debt
- [ ] `@covers` on new/changed production modules when they implement a requirement

## Delivery checklist

- [ ] Feature branch (not direct to `main`)
- [ ] Scope from `HomesFlow.prd.md` only — no invented product behavior
- [ ] Craft conventions followed (`specs/001-mvp/craft-conventions.md`)
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,34 @@ jobs:
-only-testing:HomesFlowTests \
CODE_SIGNING_ALLOWED=NO

# T089 — CI-based Sonar so sonar-project.properties multicriteria apply.
# Requires repo secret SONAR_TOKEN (SonarCloud → My Account → Security).
# Quality gate decoration still posts as "SonarCloud Code Analysis" via the
# SonarCloud GitHub App. After this job is green, disable Automatic Analysis
# in SonarCloud to avoid double scans (see sonar-disposition.md).
sonar:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check SONAR_TOKEN
id: creds
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
if [[ -z "${SONAR_TOKEN}" ]]; then
echo "present=false" >> "$GITHUB_OUTPUT"
echo "::warning::SONAR_TOKEN not set — skipping CI Sonar scan. Automatic Analysis still posts SonarCloud Code Analysis. Add Settings → Secrets → Actions → SONAR_TOKEN, then disable Automatic Analysis (see sonar-disposition.md)."
else
echo "present=true" >> "$GITHUB_OUTPUT"
fi
- name: SonarCloud Scan
if: steps.creds.outputs.present == 'true'
uses: SonarSource/sonarqube-scan-action@v5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

craft-gate:
runs-on: ubuntu-latest
needs: [shellcheck, traceability, ios]
Expand All @@ -69,3 +97,4 @@ jobs:
exit 1
fi
echo "craft-gate: shellcheck, traceability, and ios all passed"
echo "Note: SonarCloud is a separate required check (job 'sonar' + App decoration)."
7 changes: 4 additions & 3 deletions .sonarcloud.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Automatic-analysis scope (see sonar-disposition.md).
# Note: issue suppressions (multicriteria) are NOT read from properties files;
# configure those in SonarCloud UI or switch to CI-based analysis.
# Legacy automatic-analysis scope (see sonar-disposition.md).
# Prefer CI-based analysis (workflow job `sonar`) so multicriteria in
# sonar-project.properties apply. Disable Automatic Analysis in SonarCloud
# after the CI job is green.
sonar.sources=ios/HomesFlow,scripts
sonar.tests=ios/HomesFlowTests,ios/HomesFlowUITests
sonar.exclusions=supabase/**,specs/**,docs/**,archive/**,.specify/**,**/*.md,**/*.svg,**/*.png,**/*.xcconfig,scripts/**/*.py
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,21 @@ export SPECIFY_FEATURE_DIRECTORY=specs/001-mvp
| 5 | `/speckit.analyze` | Gate: must pass before coding |
| 6 | `/speckit.implement` | `ios/`, `supabase/` |

## Delivery workflow

```text
feature branch → pull request → craft-gate + SonarCloud green → merge to main
```

Do **not** push product or craft changes straight to `main`. Use a PR so Gate 2, craft checks, and Sonar run before merge. Branch protection setup: `bash scripts/finish-phase-e.sh` (admin). Details: `specs/001-mvp/craft-conventions.md` § Delivery workflow.

## Quality checks

| Gate | What | CI |
|------|------|-----|
| **Gate 0** | Build + unit tests + SwiftLint + shellcheck | `.github/workflows/ci.yml` |
| **Gate 2** | Golden thread (`check-traceability.sh`) | same workflow (ubuntu job) |
| **SonarCloud** | Static analysis ([dashboard](https://sonarcloud.io/project/overview?id=rdryfoos_HomesFlow)); policy in `sonar-project.properties` | SonarCloud on push |
| **Gate 0** | Build + unit tests + SwiftLint + shellcheck | `.github/workflows/ci.yml` (`craft-gate`) |
| **Gate 2** | Golden thread (`check-traceability.sh`) | same workflow (`traceability` job) |
| **SonarCloud** | Static analysis ([dashboard](https://sonarcloud.io/project/overview?id=rdryfoos_HomesFlow)); policy in `sonar-project.properties` | CI job `sonar` (`SONAR_TOKEN`) |

Local:

Expand Down
68 changes: 68 additions & 0 deletions scripts/finish-phase-e.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# Finish Craft Phase E: PR-only merge to main + required craft/Sonar checks.
# Usage: bash scripts/finish-phase-e.sh
#
# Prerequisites:
# - gh authenticated with admin rights on the repo
# - craft-gate + SonarCloud Code Analysis have run successfully on main at least once
# - Prefer CI-based Sonar (T089) so git multicriteria apply; see sonar-disposition.md
set -euo pipefail

REPO="rdryfoos/HomesFlow"
BRANCH="main"
CRAFT_CHECK="craft-gate"
SONAR_CHECK="SonarCloud Code Analysis"

if ! command -v gh >/dev/null 2>&1; then
echo "gh CLI required" >&2
exit 1
fi

if ! gh auth status >/dev/null 2>&1; then
echo "Run: gh auth login" >&2
exit 1
fi

echo "=== Phase E finish — branch protection on ${BRANCH} ==="
echo "Repo: ${REPO}"
echo "Required checks: ${CRAFT_CHECK}, ${SONAR_CHECK}"
echo "PR required before merge; enforce_admins=true (no silent bypass)"
echo

gh api \
--method PUT \
"repos/${REPO}/branches/${BRANCH}/protection" \
--input - <<EOF
{
"required_status_checks": {
"strict": true,
"checks": [
{"context": "${CRAFT_CHECK}"},
{"context": "${SONAR_CHECK}"}
]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"dismiss_stale_reviews": true,
"require_code_owner_reviews": false,
"required_approving_review_count": 0
},
"restrictions": null,
"allow_force_pushes": false,
"allow_deletions": false,
"required_conversation_resolution": false
}
EOF

echo
echo "✓ Branch protection updated for Phase E"
echo
echo "Verify: https://github.com/${REPO}/settings/branches"
echo "Expected:"
echo " - Require a pull request before merging (0 approvals OK for solo)"
echo " - Require status checks: ${CRAFT_CHECK}, ${SONAR_CHECK}"
echo " - Require branches to be up to date"
echo " - Do not allow bypassing the above settings (enforce admins)"
echo
echo "Break-glass: temporarily disable enforce_admins in settings, then re-run this script."
echo "=== Done ==="
37 changes: 29 additions & 8 deletions specs/001-mvp/craft-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,34 @@ Product rigor answers *what* we build and *how we prove it* (Gate 2). Craft rigo

## Hierarchy

1. **Constitution** + **PRD** — product law
2. **traceability.md** — golden thread (Gate 2)
3. **This file** — Swift, shell, and static-analysis policy
4. **sonar-project.properties** — SonarCloud configuration (must match this file)
5. **sonar-disposition.md** — bulk Sonar waivers with rationale
1. **PRD** — product scope (what to build)
2. **Constitution** — process / architecture law (how we build)
3. **traceability.md** — golden thread (Gate 2)
4. **This file** — Swift, shell, and static-analysis policy
5. **sonar-project.properties** — SonarCloud configuration (must match this file)
6. **sonar-disposition.md** — bulk Sonar waivers with rationale

When Sonar and Gate 2 disagree, **Gate 2 wins** for tests; **Supabase API shape wins** for DTO naming.
When Sonar and Gate 2 disagree, **Gate 2 wins** for tests; **Supabase API shape wins** for DTO naming. Product scope vs process: see constitution Hierarchy of Truth.

---

## Delivery workflow (Craft Phase E)

Target path for every change:

```text
feature branch → open PR → craft-gate + SonarCloud green → merge to main
```

| Rule | Detail |
|------|--------|
| No direct push to `main` | Branch protection requires a PR (`scripts/finish-phase-e.sh`) |
| Required checks | `craft-gate` (shellcheck + Gate 2 + iOS) and `SonarCloud Code Analysis` |
| PR template | `.github/pull_request_template.md` — task IDs, Traces, Gate 2 evidence |
| Scope | From `HomesFlow.prd.md` only; silent PRD → ask or propose a PRD change |
| Agents | Same rules; do not bypass gates via admin push |

Break-glass (emergency only): temporarily turn off “Do not allow bypassing…” in GitHub branch settings, fix forward, then re-run `bash scripts/finish-phase-e.sh`.

---

Expand Down Expand Up @@ -72,9 +93,9 @@ When Sonar and Gate 2 disagree, **Gate 2 wins** for tests; **Supabase API shape
| **Gate 2** | `bash scripts/check-traceability.sh` | Linux or macOS |
| **Shell** | `shellcheck scripts/*.sh` | Linux or macOS |
| **SwiftLint** | `swiftlint lint --config ios/.swiftlint.yml` | macOS |
| **Sonar** | SonarCloud on push (quality gate on **new code** after Phase D) | SonarCloud |
| **Sonar** | CI job `sonar` in `.github/workflows/ci.yml` (`SONAR_TOKEN`) + quality gate on **new code** | Ubuntu + SonarCloud |

SwiftLint intentionally disables size/complexity rules on the existing codebase; opt-in rules target `force_try`, `force_cast`, and similar footguns. Sonar policy excludes migrations and suppresses conventions documented in `sonar-disposition.md`.
SwiftLint intentionally disables size/complexity rules on the existing codebase; opt-in rules target `force_try`, `force_cast`, and similar footguns. Sonar policy excludes migrations and suppresses conventions documented in `sonar-disposition.md`. CI-based scan reads `sonar.issue.ignore.multicriteria` from `sonar-project.properties`.

---

Expand Down
10 changes: 5 additions & 5 deletions specs/001-mvp/dev-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ T076 (2026-07-03): `StructuralActionPolicy` implements AC-SYNC-07 — structural
| **Craft Phase B** | CI: shellcheck + Gate 2 + SwiftLint + build + `HomesFlowTests` | **Done** (2026-07-08) — `.github/workflows/ci.yml` |
| **Craft Phase C** | Tighten SwiftLint incrementally (re-enable size/complexity rules as files are split); optional `unused_parameter` as warning-as-error | **Next** (opportunistic) |
| **Craft Phase D** | SonarCloud suppressions (UI) + GitHub branch protection on `main` | **Done** (2026-07-08) |
| **Craft Phase E** | PR-only merge + PR template + CI-based Sonar scan | **Next** — tasks T087–T090 |
| **Craft Phase E** | PR-only merge + PR template + CI-based Sonar scan | **In progress** — T088–T090 done; T087 via `finish-phase-e.sh` after merge |
| Observability | Sentry crash telemetry (DSN-gated via `SENTRY_DSN`) | **Done** — optional until TestFlight |
| Regression evals | Scripted sync/conflict scenario datasets in CI (SC-04 matrix) | **Done** — `SyncConflictMatrixTests` |

Expand All @@ -249,10 +249,10 @@ Pre-release sign-off: [`release-checklist.md`](./release-checklist.md) per `trac

**Phase E** — PR workflow + CI Sonar (tracked in [tasks.md](./tasks.md) Phase 14):

1. **T087** — PR-only merge to `main`; remove reliance on admin bypass (gates exist but direct push still works for repo admins)
2. **T088** — PR template: task ID(s), traced AC/FR IDs, test/Gate 2 evidence
3. **T089** — Sonar from CI so git `multicriteria` in `sonar-project.properties` is authoritative (automatic analysis ignores them today)
4. **T090** — Document branch → PR → merge in `craft-conventions.md` and README
1. ~~**T088** — PR template~~ ✅ `.github/pull_request_template.md`
2. ~~**T090** — Document branch → PR → merge~~ ✅ `craft-conventions.md` + README
3. ~~**T089** — Sonar from CI~~ ✅ job `sonar` in `ci.yml` (needs `SONAR_TOKEN`; then disable Automatic Analysis)
4. **T087** — PR-only merge + `enforce_admins` — run after merge: `bash scripts/finish-phase-e.sh`

Target flow: feature branch → open PR → `craft-gate` + Sonar green on PR → merge → `main` protected without bypass.

Expand Down
30 changes: 23 additions & 7 deletions specs/001-mvp/sonar-disposition.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
# SonarCloud Disposition: HomesFlow MVP

**Project**: [rdryfoos_HomesFlow](https://sonarcloud.io/project/overview?id=rdryfoos_HomesFlow)
**Policy files**: `sonar-project.properties` (CI-based analysis) · `.sonarcloud.properties` (automatic analysis scope)
**Policy files**: `sonar-project.properties` (CI-based analysis — authoritative) · `.sonarcloud.properties` (legacy automatic-analysis scope)
**Craft context**: `craft-conventions.md`

SonarCloud reports **code smells only** (no bugs/vulnerabilities at baseline). Most findings were **tool misconfiguration**, not craft failures.

> **Automatic analysis limitation**: SonarCloud ignores `sonar.issue.ignore.multicriteria` from properties files. Until we switch to CI-based scan, enter the suppressions below in **Project Administration → General Settings → Analysis Scope → Ignore Issues on Multiple Criteria** (same patterns as git).
## Analysis mode (Craft Phase E / T089)

| Mode | Status | Notes |
|------|--------|-------|
| **CI-based** (preferred) | Job `sonar` in `.github/workflows/ci.yml` | Reads git `sonar.issue.ignore.multicriteria`; needs `SONAR_TOKEN` secret |
| Automatic analysis | Disable after CI Sonar is green | Ignores multicriteria from properties files |

**Setup**

1. Create a SonarCloud **analysis token** (My Account → Security).
2. Add repo secret `SONAR_TOKEN` (Settings → Secrets and variables → Actions).
3. Confirm the `sonar` job runs the scan (not the skip warning) and is green on a PR.
4. In SonarCloud → Administration → Analysis Method, **turn off Automatic Analysis** so only CI scans run (avoids double analysis and keeps suppressions in git).

Until `SONAR_TOKEN` exists, the CI `sonar` job **skips with a warning**; Automatic Analysis continues to decorate PRs as `SonarCloud Code Analysis`.

UI multicriteria rows (below) can remain as belt-and-suspenders until automatic analysis is off; after that, git is source of truth.

---

## Configured suppressions (in git + Sonar UI)
## Configured suppressions (in git; mirror in Sonar UI until automatic analysis is off)

| Rule key pattern | File path pattern | Count (baseline) | Rationale |
|------------------|-------------------|----------------:|-----------|
| **swift:S100** | `**/HomesFlowTests/**` | 109 | Gate 2 requires `test_AC_*` snake_case names |
| **swift:S115** | `**/ios/**` | 26 | Supabase JSON uses `snake_case` field names |
| **swift:S1075** | `**/HomesFlowTests/**` | 7 | Test fixture URIs, not production config |
| **swift:S1186** | `**/ios/HomesFlow/Features/**` | 13 | SwiftUI dismiss-only closures |
| *(scope)* | `supabase/**` excluded | 19 plsql | Immutable migrations — via `.sonarcloud.properties` |
| *(scope)* | `supabase/**` excluded | 19 plsql | Immutable migrations — via properties exclusions |

---

Expand All @@ -40,12 +56,12 @@ After the next analysis, bulk-close any remaining **S1186** outside `Features/`

## Quality gate target (new code)

Once baseline is calibrated:

- **0** new bugs, vulnerabilities, blocker issues
- **0** new critical on `ios/HomesFlow/**` (excluding configured suppressions)
- Legacy debt on old code: trend down, not zero

Branch protection requires the GitHub check **SonarCloud Code Analysis** (posted by the SonarCloud GitHub App after CI or automatic analysis).

---

## Maintenance
Expand All @@ -54,4 +70,4 @@ When adding a convention that Sonar fights:

1. Update `craft-conventions.md`
2. Add a `sonar.issue.ignore.multicriteria` entry with a one-line rationale
3. Note the change here — do not hand-edit Sonar UI without updating git
3. Note the change here — do not rely on Sonar UI alone once CI analysis is the source of truth
12 changes: 6 additions & 6 deletions specs/001-mvp/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Input**: [spec.md](./spec.md) · [plan.md](./plan.md) · [data-model.md](./data-model.md) · [contracts/](./contracts/)

**Feature**: `001-mvp` | **Updated**: 2026-07-09
**Feature**: `001-mvp` | **Updated**: 2026-07-17

**UI reference** (non-authoritative): https://haze-rabbit-58180688.figma.site — SwiftUI-native iPhone/iPad only.

Expand All @@ -19,7 +19,7 @@
| 11 Hardening | Not started | Re-run analyze after P1 checkpoint |
| 12 Conflict model evolution | **Complete** | — |
| 13 Log Book | **Complete** | — |
| 14 Delivery hygiene (craft Phase E) | Not started | PR-only merge; CI Sonar scan |
| 14 Delivery hygiene (craft Phase E) | **In progress** | PR template + CI Sonar landed; run `finish-phase-e.sh` for protection flip |

Partial deliverables documented in [dev-notes.md](./dev-notes.md). **Do not** encode implementation details in [spec.md](./spec.md).

Expand Down Expand Up @@ -301,10 +301,10 @@ Partial deliverables documented in [dev-notes.md](./dev-notes.md). **Do not** en

**Goal**: PR-based delivery to `main` with the same gates that already run on push — no silent admin bypass; Sonar suppressions authoritative in git.

- [ ] T087 [P] Enforce PR-only merge to `main`: branch protection requires pull request, `craft-gate` + `SonarCloud Code Analysis`, and disallow admin bypass — **Traces**: plan Phase 0 (infrastructure), NFR-SEC-01 — *today: direct push to main still possible as admin; see dev-notes craft Phase E*
- [ ] T088 [P] Add `.github/pull_request_template.md`: task ID(s), `Traces:` AC/FR IDs, test evidence, Gate 2 status — **Traces**: plan Phase 0 (infrastructure) — *golden thread visible at review time*
- [ ] T089 [P] Run SonarCloud from CI (`.github/workflows/ci.yml` or dedicated job) so `sonar-project.properties` multicriteria suppressions apply; retire automatic-analysis-only suppressions where redundant — **Traces**: plan Phase 0 (infrastructure) — *see sonar-disposition.md automatic-analysis limitation*
- [ ] T090 [P] Document delivery workflow in `craft-conventions.md` + README: feature branch → PR → green gates → merge; link `finish-phase-d.sh` for protection setup — **Traces**: plan Phase 0 (infrastructure)
- [ ] T087 [P] Enforce PR-only merge to `main`: branch protection requires pull request, `craft-gate` + `SonarCloud Code Analysis`, and disallow admin bypass — **Traces**: plan Phase 0 (infrastructure), NFR-SEC-01 — *script ready: `bash scripts/finish-phase-e.sh` (admin); run after this PR merges and CI Sonar is green*
- [x] T088 [P] Add `.github/pull_request_template.md`: task ID(s), `Traces:` AC/FR IDs, test evidence, Gate 2 status — **Traces**: plan Phase 0 (infrastructure) — *golden thread visible at review time*
- [x] T089 [P] Run SonarCloud from CI (`.github/workflows/ci.yml` or dedicated job) so `sonar-project.properties` multicriteria suppressions apply; retire automatic-analysis-only suppressions where redundant — **Traces**: plan Phase 0 (infrastructure) — *job `sonar` + `SONAR_TOKEN`; disable Automatic Analysis in SonarCloud UI after first green CI scan*
- [x] T090 [P] Document delivery workflow in `craft-conventions.md` + README: feature branch → PR → green gates → merge; link `finish-phase-e.sh` for protection setup — **Traces**: plan Phase 0 (infrastructure)

---

Expand Down
Loading