Compare commits

..

4 Commits

Author SHA1 Message Date
b93f9759d2 chore: ignore editor swap files
A stray .CLAUDE.md.swp got picked up by `git add -A` while writing the
contributing docs. The CLAUDE.md ignore is anchored (/CLAUDE.md), so it
never covered vim's sibling swap file.
2026-07-30 10:14:42 +02:00
9cccf0f513 chore(forge): move issue templates to .forgejo/, add config + PR template
The workflow split draws the line at secrets: .forgejo/ is what Codeberg
sees and contributors can trigger, .gitea/ is build infrastructure. Issue
templates were the one contributor-facing thing still living in .gitea/.
Forgejo searches .forgejo -> .gitea -> .github -> docs and stops at the
first hit, so this is a no-op on Codeberg and merely makes the convention
true; Gitea loses the templates, which is correct, as issues aren't filed
there.

Fixes a label that never applied: crash_report.md asked for priority:high,
but the label is priority/high, so every crash report has been landing
unprioritised.

The bug template now asks which channel the app came from and which
account type the affected calendar uses. Provider behaviour diverges
sharply between Google, CalDAV, local and subscribed calendars — a
disproportionate share of past bugs turned out to be one account type
only — and the two published channels can be versions apart. Time zone is
requested for date and all-day problems, which is where it has mattered
every time.

Adds an ISSUE_TEMPLATE config.yml pointing translators at Weblate before
they open an issue here, plus a PR template that asks for the linked issue
and flags the two easy mistakes: an undiscussed feature, and targeting
main instead of the milestone's release branch. Blank issues stay enabled —
the ToDo label exists for what the templates don't cover.
2026-07-30 10:14:18 +02:00
048de67350 docs: correct stale build, forge and layer facts
BUILDING.md never mentioned the floret-kit submodule, so its instructions
could not produce a working build — a plain clone fails to configure. It
also understated the SDK requirement: compileSdk is 37, not the "36+" it
claimed. Both matter now that the Codeberg move is meant to make an
outside clone viable.

The docs map advertised superpowers/specs/ and superpowers/plans/, neither
of which exists any more, while omitting BUILDING.md and RELEASING.md.
Dropped the dead rows and listed what is actually there. Purged the
remaining superpowers references in .planning/ and ARCHITECTURE.md's
dangling "see plan 03" the same way — a pointer to a deleted file is worse
than no pointer.

ARCHITECTURE.md still called OptionCard "the app's only sanctioned
selection-dialog style", which the 2026-07-07 flip to full-screen pickers
reversed, and described no layer for floret-kit despite it being a
compiled-from-source composite build. Its CI note credited Gitea Actions;
contributor CI runs on Codeberg now.

PROJECT.md's Source section described the old world outright ("hosted on
self-hosted Gitea") and now records the split: Codeberg canonical, Gitea
build infrastructure.
2026-07-30 10:13:48 +02:00
fbb14f9334 docs: add a contributing guide
Codeberg became canonical for issues, PRs and releases, but there was
nothing telling a contributor how any of it works — no CONTRIBUTING.md at
all, and no contributor entry point in the README.

The workflow it documents is issue-first: a feature needs a yes-or-no
before it needs code, since whether Calendula should do a thing is the one
decision a patch cannot make. Both features and bugs get a milestone, and
that milestone names the branch a PR targets (2.18.0 -> release/v2.18.0),
because main is a release trigger rather than a staging area. Typo and docs
fixes skip straight to a PR — an issue-per-typo helps nobody.

Two traps get their own sections because both waste a contributor's whole
afternoon: translations never go through PRs (Weblate owns every values-*
file, German included, and overwrites hand-edits on the next sync), and a
clone without --recurse-submodules cannot configure at all, since
floret-kit is a composite build compiled from source.

The rules section is the invariants that actually turn into review
comments — no network, no second database, no hand-patching UI state after
a write, domain/ free of Android imports, JVM-first tests, and the
reproducible-build flags that the official F-Droid repo depends on.
2026-07-30 10:13:36 +02:00
17 changed files with 395 additions and 50 deletions

View File

@@ -0,0 +1,30 @@
---
name: Bug report
about: Something doesn't work the way it should
title: ""
labels:
- bug
---
### What happened
### What you expected
### Steps to reproduce
1.
2.
3.
### Environment
- Calendula version: <!-- Settings → bottom of the screen -->
- Android version:
- Device:
- Installed from: <!-- official F-Droid / the self-hosted repo / built from source -->
- Affected calendar: <!-- Google, CalDAV (DAVx5, Nextcloud, …), on-device/local,
subscribed/WebCal, birthdays — provider behaviour differs
a lot per account type, so this often points straight at
the cause -->
- Time zone: <!-- only if the problem involves dates or all-day events -->

View File

@@ -0,0 +1,18 @@
# Kept enabled so anything that doesn't fit the four templates still has a way
# in (the `ToDo` label exists for exactly those).
blank_issues_enabled: true
contact_links:
- name: Translate Calendula
url: https://weblate.dev.jeanlucmakiola.de/engage/calendula/
about: >-
Translations are managed on Weblate, not here — it owns every values-*
file, so a hand-edited translation gets overwritten on the next sync.
No coding needed: pick or request a language and translate in the browser.
- name: Contributing guide
url: https://codeberg.org/jlmakiola/calendula/src/branch/main/CONTRIBUTING.md
about: >-
Before opening a pull request: the issue-first workflow, which release
branch to target, how to build (there's a submodule), and the
architectural rules a change is reviewed against.

View File

@@ -5,7 +5,7 @@ title: "Crash: "
labels:
- bug
- crash
- priority:high
- priority/high
---
<!--

View File

@@ -0,0 +1,41 @@
<!--
Thanks for contributing to Calendula!
Please skim CONTRIBUTING.md if you haven't:
https://codeberg.org/jlmakiola/calendula/src/branch/main/CONTRIBUTING.md
Two things it's easy to get wrong:
• Features need a discussed issue first — an undiscussed feature PR may be
closed unmerged even when the code is good.
• Target the release branch for your issue's milestone (milestone 2.18.0 →
release/v2.18.0), not main. If you targeted main, just say so below and it
will be retargeted.
-->
### What this changes
### Why
<!-- Closes #123 — link the issue this implements or fixes. -->
### How it was tested
<!--
Which of these ran green, and anything you exercised by hand. On-device notes
are especially useful for UI changes.
./gradlew lint test assembleDebug
python3 scripts/check_translations.py
-->
### Checklist
- [ ] There's an issue for this, and (for a feature) it got a go-ahead
- [ ] Targeting the release branch for that issue's milestone — or `main`, noted above
- [ ] `./gradlew lint test assembleDebug` passes locally
- [ ] No `values-*/strings.xml` touched (Weblate owns those; new English strings in `values/` are fine)
- [ ] `CHANGELOG.md` updated under `## [Unreleased]`, if the change is user-visible
- [ ] No planning or design documents committed

View File

@@ -1,23 +0,0 @@
---
name: Bug report
about: Something doesn't work the way it should
title: ""
labels:
- bug
---
### What happened
### What you expected
### Steps to reproduce
1.
2.
3.
### Environment
- Calendula version: <!-- Settings → bottom of the screen -->
- Android version:
- Device:

5
.gitignore vendored
View File

@@ -50,6 +50,11 @@ google-services.json
.DS_Store
Thumbs.db
# Editor swap/backup files
*.swp
*.swo
*~
# F-Droid local artifacts (the pipeline generates them in CI)
/fdroid/

View File

@@ -40,5 +40,9 @@ shows a stylized "1" on a slate squircle.
## Source
Hosted on self-hosted Gitea, released through self-hosted F-Droid repo on
Hetzner. Same infrastructure as `HouseHoldKeaper`.
**Codeberg (`jlmakiola/calendula`) is canonical** — git, issues, PRs, tags and
releases, plus contributor CI. The self-hosted Gitea instance is build
infrastructure only: it holds the signing key, runs the release pipeline, and
publishes the self-hosted F-Droid repo on Hetzner. Codeberg push-mirrors `main`
and tags to Gitea, where a bumped `versionName` triggers the release. Also
published to the official F-Droid repo. See `docs/RELEASING.md`.

View File

@@ -1,6 +1,6 @@
# Calendula — Requirements
See full design spec: `docs/superpowers/specs/2026-06-08-calendar-app-design.md`
Architecture reference: [`../docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md)
## V1 Scope (Variant "B") — shipped in full (v1.0.0, 2026-06-11)

View File

@@ -55,9 +55,8 @@ after v0.6 (full event read) plus the onboarding-screen polish pass.
## v2.0 — Write Support (complete, shipped 2026-06-11)
Delivered in four releasable slices (plan:
`docs/superpowers/plans/2026-06-11-03-write-support.md`). The V1 spec is a
guide here, not a contract — scope per slice is decided as we go.
Delivered in four releasable slices. The V1 spec is a guide here, not a
contract — scope per slice is decided as we go.
| Version | Milestone | Status |
|---|---|---|
@@ -244,8 +243,7 @@ pass on the existing controls; new toggles ride in with their own features.
sequential branches: `feat/ics-export` (write side + UID-on-create precursor)
then `feat/ics-import` (parser, restore, dedup by UID). Import is
liberal-in/strict-out: skip-and-report foreign `VTIMEZONE` / `RECURRENCE-ID`
/ guest lists it can't model. Plans:
`docs/superpowers/plans/2026-06-18-05-ics-export.md` + `…-06-ics-import.md`.
/ guest lists it can't model.
11. **Snooze / dismiss notification actions** *(merged into release/v2.8.0)*
followed the `.ics` work; inherits v2.6's deferred exact-alarm/WorkManager
decision (snooze must re-fire an alarm).

View File

@@ -25,7 +25,7 @@ release-cut.
## Progress
- [x] Design spec written and committed (`docs/superpowers/specs/2026-06-08-calendar-app-design.md`)
- [x] Design spec written and committed (2026-06-08)
- [x] V1 design decisions resolved (App name "Calendula", icon, seed color)
- [x] Plan 01 written and executed — foundation lands (theme, icon, i18n, Hilt, DataStore, CI green)
- [x] Plan 02 written and executed — data layer + permission flow + debug screen

192
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,192 @@
# Contributing to Calendula
Calendula is a Material 3 Expressive calendar app that lives strictly on top of
Android's `CalendarContract` — no app database, no sync stack, no network access.
That constraint shapes most review comments, so
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) is worth skimming before you write
code. This file is the practical how.
**[Codeberg](https://codeberg.org/jlmakiola/calendula) is the canonical home** —
issues, pull requests, releases. The self-hosted Gitea instance referenced in the
release docs is build infrastructure only; there is nothing to contribute there.
Be decent to the people you meet in the tracker.
## Start with an issue
| You want to | Do this |
|---|---|
| Add a feature | **Open an issue first** and wait for a go-ahead |
| Fix a bug | Open an issue, then a pull request |
| Fix a typo, a comment, or docs | Just open the pull request |
| Add or fix a translation | **Don't** — [use Weblate](#translations) |
Features get an opinion before they get code: whether Calendula should do a
thing at all is the one decision a patch can't make. A feature PR that arrives
without a discussed issue may be closed unmerged even when the code is good —
please don't spend a weekend on one first.
Bugs are more straightforward, but still start with an issue: it's what carries
the milestone and gives the changelog something to link.
Issue templates cover bug, crash, feature and question. For a crash, let the app
do the work — **Settings → Report a problem**, or the prompt shown after a crash,
captures the stack trace and prefills the form. The report contains app, Android
and device versions plus the trace; no calendar content, no personal data.
## Which branch to target
Calendula releases by merging a version bump into `main`, so `main` is a release
trigger rather than a staging area. Work is assembled on release branches first.
Once your issue has a milestone, that milestone names your branch:
| Milestone | Target branch |
|---|---|
| `2.18.0` | `release/v2.18.0` |
Every milestone has a matching branch. If it's somehow missing, target `main` and
mention it in the PR — it will be retargeted. Don't pick an older release branch:
they're kept after shipping, so the newest one isn't necessarily yours.
## Translations
**Never edit a `values-*/strings.xml` file in a pull request** — German included.
Translations are owned by a self-hosted Weblate that writes to this repository
directly, and a hand-edit is overwritten on the next sync.
**[Translate Calendula on Weblate](https://weblate.dev.jeanlucmakiola.de/engage/calendula/)**
Adding a *new* English string to `values/strings.xml` is normal PR work; Weblate
picks it up and offers it to translators. Partial translations are expected and
fine — missing keys are informational. Stale and orphaned keys are not, so run
```sh
python3 scripts/check_translations.py
```
before pushing. It reports those more clearly than lint's `MissingTranslation`
does.
## Build & test
```sh
git clone --recurse-submodules https://codeberg.org/jlmakiola/calendula.git
```
The `floret-kit` submodule is a composite build compiled from source. An
existing clone needs `git submodule update --init --recursive`, or nothing
resolves.
- **JDK 17** — not newer; the Android Gradle Plugin requires exactly 17. Set
`JAVA_HOME` if your default differs.
- **Android SDK** — platform 37 (`compileSdk`) and build-tools 36.0.0, located
via `ANDROID_HOME` or a gitignored `local.properties` with `sdk.dir`. If you
go the `local.properties` route the included build needs its own copy at
`floret-kit/local.properties`; `ANDROID_HOME` covers both at once and is the
easier path.
The Gradle wrapper is checked in, so no system Gradle is needed.
```sh
./gradlew lint test assembleDebug # roughly what CI runs
```
A single test class, or a pattern:
```sh
./gradlew testDebugUnitTest --tests "de.jeanlucmakiola.calendula.domain.SimpleRecurrenceTest"
./gradlew testDebugUnitTest --tests "*SimpleRecurrence*"
```
CI reports one `CI` check per pull request: `lintDebug`, `testDebugUnitTest`,
`assembleDebug`, and a Trivy scan. Pull requests touching only docs, F-Droid
metadata or the licence skip the Android build and go green quickly. More detail
in [`docs/BUILDING.md`](docs/BUILDING.md).
## The rules
These are the ones that turn into review comments.
1. **No network.** Calendula holds no `INTERNET` permission, and that's a
feature rather than an oversight. Anything that would need one is a product
decision before it's a patch — the crash reporter deliberately opens a
prefilled web issue instead of posting anything itself.
2. **The provider is the only database.** No Room, no cache, no local mirror of
events. `CalendarContract` is the single source of truth, which is also why
externally synced changes work for free.
3. **Don't patch UI state after a write.** A `ContentObserver` re-queries and
views recompose from fresh provider state. Hand-patching a list after saving
appears to work, then quietly diverges from what the provider actually stored.
4. **`domain/` has no Android imports.** Models, validation, recurrence
rendering, conflict snapshots and the `.ics` codec stay pure Kotlin so they
remain JVM-testable.
5. **Tests run on the JVM.** JUnit 5 + Truth + Turbine. The seams exist for you:
fake the data source (`FakeCalendarDataSource`), and feed mappers plain maps
through `ColumnReader` instead of cursors. Instrumented tests are a last
resort, not a default.
6. **Read before touching the subtle pipelines.** Recurring writes (UNTIL vs
DURATION, exception URIs, series splits), save-conflict detection and reminder
delivery (post-before-mark) follow provider-driven rules that are documented
in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) and are not guessable from
the code alone.
7. **Don't break reproducible builds.** `vcsInfo`, `dependenciesInfo` and the AGP
metadata block are disabled on purpose so the official F-Droid repo can verify
our binary against a from-source rebuild.
`scripts/check_reproducible_release.sh` runs on every pull request, including
docs-only ones.
## UI conventions
Material 3 Expressive throughout, built from the system's own tokens and
components — colour-scheme tokens rather than hardcoded colours, `ListItem` for
settings rows.
**Selection pickers are full-screen.** Every browse-style "choose one" surface
uses floret-kit's `FullScreenPicker` / `OptionPicker`; one that needs a commit or
extra action passes it through the picker's `actions` slot. The exception is the
recurring-scope chooser (*this / this and following / all*), which stays a
compact dialog — a two- or three-option decision reads better as a popup than as
a nearly empty screen. `AlertDialog` is for plain confirmations only, and radio-
or text-list dialogs aren't used at all.
Shared UI machinery lives in the `floret-kit` submodule and has
[its own contributing guide](https://codeberg.org/jlmakiola/floret-kit/src/branch/main/CONTRIBUTING.md);
changing it means a pull request against that repository plus a submodule bump
here.
## Commits & pull requests
Conventional commits, scoped to the area you touched:
```
fix(calendars): keep an event's own calendar when it is switched off
feat(month): pull-to-expand the split view (#38)
docs(architecture): record what the second review pass changed
```
Types in use: `feat` `fix` `docs` `refactor` `style` `chore` `ci` `build`
`revert`. Reference the issue in the subject or the body. Keep commits small —
small commits revert cleanly, which matters more here than a tidy history.
If your change is user-visible, add an entry under `## [Unreleased]` in
[`CHANGELOG.md`](CHANGELOG.md). Match the surrounding voice: entries describe
what changed *for the person using the app*, and why, not what changed in the
code. Link the issue and add its reference at the bottom of the file. It may get
reworded when the release is cut, so don't agonise over it.
Please don't commit planning or design documents. Code, tests, architecture notes
and the changelog land; the reasoning belongs in the commit message and the
issue.
## Not going to happen
Saving you the effort:
- **Drag-and-drop event rescheduling.** Considered repeatedly, settled as a no.
- **Anything requiring `INTERNET`**, including a sync stack of our own.
- **An app-side event database.**
## Licence
Calendula is [MIT](LICENSE). By contributing you agree your changes ship under
the same licence.

View File

@@ -105,10 +105,26 @@ without reinstalling. Or build from source — see below.
## 📚 Documentation
- **[Contributing](CONTRIBUTING.md)** — how to report, propose, and patch
- **[Building from source](docs/BUILDING.md)** — requirements and Gradle tasks
- **[Architecture](docs/ARCHITECTURE.md)** — the layered design and key pipelines
- **[Roadmap](.planning/ROADMAP.md)** — what's shipped and what's next
## 🤝 Contributing
Bug reports, ideas, and patches are all welcome on
**[Codeberg](https://codeberg.org/jlmakiola/calendula/issues)**.
The short version: **start with an issue.** Features get a yes-or-no before they
get code, and both features and bugs are assigned a milestone whose
`release/vX.Y.Z` branch your pull request then targets. Typo and docs fixes can
skip straight to a pull request. Translations don't go through pull requests at
all — [Weblate owns them](#-translations).
Read **[CONTRIBUTING.md](CONTRIBUTING.md)** before writing code: it covers the
workflow, the build (note the `floret-kit` submodule), and the architectural
rules a change is reviewed against.
## 🌍 Translations
Calendula ships in German and English, and you're warmly invited to add your

View File

@@ -62,9 +62,15 @@ flowchart TD
- **`data/prefs/`** — DataStore-backed settings (theme, week start, form
field defaults, reminders toggle) and small state (last-used calendar).
- **`ui/`** — one package per screen, each with Screen + ViewModel +
UiState. Shared pieces in `ui/common/` (OptionCard — the app's only
sanctioned selection-dialog style —, recurrence humanizer, FAB column,
drawer, transitions).
UiState. Shared pieces in `ui/common/` (recurrence humanizer, FAB column,
drawer, transitions). Selection pickers are full-screen and come from
floret-kit (`FullScreenPicker` / `OptionPicker`); `AlertDialog` is reserved
for plain confirmations and the compact recurring-scope choosers.
- **`floret-kit/`** — the shared Material 3 Expressive kit for the Floret app
family, wired in as a git submodule *and* a Gradle composite build
(`includeBuild`), so it is compiled from source rather than resolved as a
dependency. Pickers, crash plumbing, and locale/time helpers live there;
changing them is a pull request against that repository plus a submodule bump.
## Navigation
@@ -81,7 +87,7 @@ exactly like an event tap.
## Recurring writes
The provider's invariants drive the design (learned the hard way, verified
on-device — see plan 03):
on-device):
- Recurring rows carry `RRULE` + `DURATION` (no `DTEND`); one-off rows
carry `DTEND`.
@@ -167,6 +173,7 @@ JUnit 5 + Truth + Turbine on the JVM. The seams that make it work:
`CalendarDataSource` is faked (`FakeCalendarDataSource` records writes),
mappers parse `ColumnReader`/plain maps instead of cursors, domain logic
(recurrence, validation, snapshots, write-value building) is pure. CI
(Gitea Actions) runs `lint test assembleDebug` once per pull request; merging a
(Forgejo Actions on Codeberg) runs `lint test assembleDebug` once per pull
request; merging a
bumped `versionName` to `main` builds, signs, and publishes to the self-hosted
F-Droid repo and then mints the `vX.Y.Z` tag + release. See docs/RELEASING.md.

View File

@@ -1,12 +1,35 @@
# Building from source
Calendula builds with the standard Android Gradle toolchain — no extra setup
beyond the SDK and a JDK.
beyond the SDK, a JDK, and the submodule.
## Clone
```bash
git clone --recurse-submodules https://codeberg.org/jlmakiola/calendula.git
```
Calendula depends on **[floret-kit](https://codeberg.org/jlmakiola/floret-kit)**,
the shared Material 3 Expressive kit, as a git submodule wired in as a Gradle
composite build (`includeBuild("floret-kit")` in `settings.gradle.kts`) — it is
compiled from source, not resolved from a repository. A clone without the
submodule fails to configure. For an existing clone:
```bash
git submodule update --init --recursive
```
## Requirements
- **Android SDK 36+**
- **JDK 17** — if your default JDK is not 17, set `JAVA_HOME` explicitly.
- **JDK 17** — not newer; the Android Gradle Plugin requires exactly 17. If your
default JDK differs, set `JAVA_HOME` explicitly.
- **Android SDK** — platform **37** (`compileSdk`) and **build-tools 36.0.0**.
`minSdk` is 29, `targetSdk` 36.
The SDK is located via `ANDROID_HOME` (or `ANDROID_SDK_ROOT`), or via a
gitignored `local.properties` with `sdk.dir`. If you use `local.properties`, note
that the composite build needs **its own** copy at `floret-kit/local.properties`;
setting `ANDROID_HOME` covers both builds at once and is the simpler route.
The Gradle wrapper is checked in, so you don't need a system Gradle.
@@ -14,11 +37,44 @@ The Gradle wrapper is checked in, so you don't need a system Gradle.
```bash
./gradlew assembleDebug # debug APK → app/build/outputs/apk/debug/
./gradlew test # JVM unit tests
./gradlew test # JVM unit tests (JUnit 5 + Truth + Turbine)
./gradlew lint # Android lint
```
A single test class, or a pattern:
```bash
./gradlew testDebugUnitTest --tests "de.jeanlucmakiola.calendula.domain.SimpleRecurrenceTest"
./gradlew testDebugUnitTest --tests "*SimpleRecurrence*"
```
Translation-key invariants (stale and orphaned keys are fatal; missing ones are
not) are checked outside Gradle:
```bash
python3 scripts/check_translations.py
```
## What CI runs
`.forgejo/workflows/ci.yaml` reports a single `CI` check per pull request on
Codeberg: the reproducible-release guard, then `lintDebug`,
`testDebugUnitTest`, `assembleDebug` and a Trivy filesystem scan. Pull requests
that touch only docs, F-Droid metadata or the licence skip the Android build and
still report green.
## Release builds
Release signing and the publishing pipeline are documented separately in
[RELEASING.md](RELEASING.md).
[RELEASING.md](RELEASING.md). To smoke-test a release candidate locally, the
`releaseTest` build type is an R8-shrunk twin of `release`, debug-signed with its
own `applicationId` suffix so it installs alongside the real app:
```bash
scripts/verify-release.sh
```
## Contributing
See [`../CONTRIBUTING.md`](../CONTRIBUTING.md) for the issue-first workflow,
which branch to target, and the architectural rules a change is reviewed against.

View File

@@ -4,19 +4,20 @@ Where to look for what:
| Document | What it is |
|---|---|
| [`../CONTRIBUTING.md`](../CONTRIBUTING.md) | How to contribute: issue-first workflow, which branch to target, translations, the rules a change is reviewed against |
| [`BUILDING.md`](BUILDING.md) | Building from source: submodule, JDK/SDK requirements, Gradle tasks, what CI runs |
| [`ARCHITECTURE.md`](ARCHITECTURE.md) | Orientation tour: principles, layers, navigation, recurring-write / conflict / reminder pipelines, testing |
| [`RELEASING.md`](RELEASING.md) | Release process: versioning, the merge-driven pipeline, the two-forge split, secrets, key custody |
| [`../CHANGELOG.md`](../CHANGELOG.md) | Release history (Keep a Changelog, SemVer) |
| [`../.planning/ROADMAP.md`](../.planning/ROADMAP.md) | Living roadmap: shipped milestones, current scope, idea backlog |
| [`../.planning/PROJECT.md`](../.planning/PROJECT.md) | What the project is, stack, naming, infrastructure |
| [`../.planning/REQUIREMENTS.md`](../.planning/REQUIREMENTS.md) | Requirement checklist per milestone |
| [`../.planning/STATE.md`](../.planning/STATE.md) | Snapshot of where development currently stands |
| [`superpowers/specs/`](superpowers/specs/) | The original design spec (2026-06-08) — historical record, not updated |
| [`superpowers/plans/`](superpowers/plans/) | Per-milestone implementation plans with task checklists — historical record of how each slice was built, including provider lessons learned |
| [`design/`](design/) | Per-feature design notes kept for features whose provider behaviour is worth recording |
| [`../fastlane/metadata/android/`](../fastlane/metadata/android/) | Store metadata (single source of truth): descriptions, title, icon, screenshots (DE + EN). Harvested directly by the official F-Droid repo; transformed into the self-hosted repo layout at release time by [`../scripts/fastlane_to_fdroid_localized.sh`](../scripts/fastlane_to_fdroid_localized.sh) |
| [`../fdroid-metadata/`](../fdroid-metadata/) | App-level F-Droid control file (`*.yml`: Categories, License, links) for the self-hosted repo's `fdroid update` |
| [`fdroid-official/`](fdroid-official/) | Draft recipe + notes for publishing to the **official** F-Droid repo (reproducible build + developer-signed binary) |
| [`fdroid-official/`](fdroid-official/) | Recipe + notes for publishing to the **official** F-Droid repo (reproducible build + developer-signed binary) |
Conventions: plans and specs under `superpowers/` are point-in-time
artifacts of the agentic workflow that built each milestone — they get
status updates but are never rewritten. The `.planning/` files are living
documents and should stay current.
Conventions: the `.planning/` files are living documents and should stay current.
`ARCHITECTURE.md` is the authoritative orientation tour — it is updated with the
code, and is the right place for a lesson learned about the calendar provider.