contributing: link to rename-safety contract#57
Merged
Conversation
Add a Rename safety section to CONTRIBUTING.md linking to the canonical SUBSTRATE-CONTRACT.md in the agent repo. Includes a quick rule of thumb (avoid "a" / "an" before Shop / Shopkeeper / ItemTag) and a Rails-flavored failure-mode example (OpenAPI summaries and test descriptors). Mirrors the section added to the iOS substrate in nativeapptemplate/NativeAppTemplate-Free-iOS#67. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dadachi
added a commit
that referenced
this pull request
May 10, 2026
Reverses the previous "Number → name" / "is ready" decision once it became clear that any state-verb baked into the substrate's notifier title or class name fights the agent's domain-adapt step. The agent extends/renames the AASM state machine per spec (idled/completed → e.g. waiting/seated for restaurant, pending/seen for vet clinic), but its rename plan only handles the four model-level tokens (Shop / Shopkeeper / ItemTag / NativeAppTemplate). State names cascading into notifier file/class/locale-key/title are out of scope for the rename-safety contract (#57). So the substrate's notifier ships state-verb-free: - File: item_tag_called_notifier.rb → item_tag_notifier.rb - Class: ItemTagCalledNotifier → ItemTagNotifier - Locale key: notifiers.item_tag_called → notifiers.item_tag - Title: "%{name} is ready" → "%{name}" - Body: "Please proceed to %{shop}." → "%{shop}" `ItemTag` itself IS in the rename plan, so file/class/locale-key cascade through `item_tag → patient/reservation/todo` cleanly. `%{name}` and `%{shop}` are interpolation keys, not renameable tokens. Result: substrate copy survives any state-verb rewrite the agent's adapt step does, at the cost of vague substrate copy. The adapt step can rewrite richer per-domain copy when it wants. Tests + rubocop clean.
dadachi
added a commit
that referenced
this pull request
May 10, 2026
* Add push notifications scaffolding via noticed v2 PR #1 of 5 in #58. Scaffolds the Rails-side groundwork for native push notifications. Provider integration (APNs + FCM) and ItemTag AASM wiring follow in PR #2 once APNs .p8 and FCM service-account JSON are provisioned. Client work (free + paid iOS/Android) follows in PRs #3-5. What lands here - noticed v2 gem + the two engine migrations (Noticed::Event, Noticed::Notification, both UUID-keyed to match this substrate's primary_key_type) - Device model + migration: shopkeeper-scoped, unique on [platform, token], last_active_at for staleness scoping; ios/android enum - Api::V1::Shopkeeper::DevicesController: POST /api/v1/shopkeeper/devices — idempotent upsert (rebinds token to current_shopkeeper if it previously belonged to someone else, e.g. shared device after sign-out/sign-in); 201 on create, 200 on touch DELETE /api/v1/shopkeeper/devices/:id — unregister (404 on someone else's device, scoped via current_shopkeeper.devices) - DevicePolicy + DeviceSerializer following existing substrate conventions (BasePolicy + JSONAPI::Serializer) - ApplicationNotifier base + example ItemTagCalledNotifier (no delivery methods wired yet — title/body/url are i18n-resolved via notification_methods so PR #2 just needs to add deliver_by :ios + :android and trigger from ItemTag's AASM complete event) - Shopkeeper.has_many :devices (dependent: :destroy) and :notifications (as: :recipient, class: Noticed::Notification) - Locale entries under notifiers.item_tag_called Tests: 21 new runs (Device model 9, DevicesController 8, notifier 4), 0 failures. Full suite now 419 runs / 868 assertions / 0 failures / 0 errors / 0 skips. rubocop clean (239 files, 0 offenses). * Wire deliver_by :action_push_native via Rails-native action_push_native Install action_push_native 0.3.x and generate ApplicationPushNotification, ApplicationPushDevice, ApplicationPushNotificationJob, and config/push.yml. Add deliver_by :action_push_native to ItemTagCalledNotifier so push notifications route through Rails 8.1's Action Push Native (single abstraction over APNs + FCM) instead of the Noticed gem's per-platform :ios / :fcm deliverers. APNs/FCM credentials remain placeholders in config/push.yml — provision via bin/rails credentials:edit before enabling delivery. Bridging the existing Device registration API to ApplicationPushDevice (so registered tokens actually flow into Action Push Native delivery) is a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Generalize notifier copy: %{number} → %{name}, drop "Number" prefix Substrate post-Phase-1 (#45) is generic single-resource CRUD, not queue-only — `ItemTag.name` can be a queue number ("A001"), a pet name ("Mittens"), a task title, etc. The copy "Number %{number} is up" only reads correctly for the queue case, and the agent's renamer doesn't substitute the word "Number" (it's not in the rename plan), so the wrong copy ships to every renamed app. Change to "%{name} is ready" — generalizes cleanly across the queue, reservation, vet-clinic, and task-tracker domains the substrate targets. Body unchanged. Test passes; rubocop clean. * Generalize notifier: drop "Called" + state-verb copy (rename-resistant) Reverses the previous "Number → name" / "is ready" decision once it became clear that any state-verb baked into the substrate's notifier title or class name fights the agent's domain-adapt step. The agent extends/renames the AASM state machine per spec (idled/completed → e.g. waiting/seated for restaurant, pending/seen for vet clinic), but its rename plan only handles the four model-level tokens (Shop / Shopkeeper / ItemTag / NativeAppTemplate). State names cascading into notifier file/class/locale-key/title are out of scope for the rename-safety contract (#57). So the substrate's notifier ships state-verb-free: - File: item_tag_called_notifier.rb → item_tag_notifier.rb - Class: ItemTagCalledNotifier → ItemTagNotifier - Locale key: notifiers.item_tag_called → notifiers.item_tag - Title: "%{name} is ready" → "%{name}" - Body: "Please proceed to %{shop}." → "%{shop}" `ItemTag` itself IS in the rename plan, so file/class/locale-key cascade through `item_tag → patient/reservation/todo` cleanly. `%{name}` and `%{shop}` are interpolation keys, not renameable tokens. Result: substrate copy survives any state-verb rewrite the agent's adapt step does, at the cost of vague substrate copy. The adapt step can rewrite richer per-domain copy when it wants. Tests + rubocop clean. * Swap notifier title/body: shop in title, item name in body Push-notification UX convention is source-in-title, event-in-body — WhatsApp (sender → message), Slack (channel → message), Calendar (event → location). Shop is the recognizable persistent entity that anchors the notification; item name is variable per-event content. Title: %{name} → %{shop} Body: %{shop} → %{name} Tests + rubocop clean. * credentials.yml.tt: add action_push_native APNs + FCM placeholders config/push.yml looks up Rails.application.credentials.dig( :action_push_native, :apns, :key_id) and friends, but the credentials template that seeds `bin/rails credentials:edit` on first generation didn't expose those keys. Fresh developers would hit silent nil on first push delivery without knowing where the lookup expected the secret. Adds the same shape Resend's api_key already follows: empty placeholder under the documented key path. Comment notes which inputs are needed (APNs key_id + .p8 contents, FCM service-account JSON). * push.yml: move team_id/topic/project_id to credentials too Three deployment-specific values were still hard-coded as placeholders in config/push.yml: - apple.team_id (Apple Developer team identifier — per-deployer) - apple.topic (iOS bundle identifier — per-deployment) - google.project_id (Firebase project identifier — per-deployment) These don't belong in source. apple.topic in particular is a rename- pipeline trap: the agent renames the iOS bundle id when generating a domain-customized variant (com.nativeapptemplate.* → com.<spec>.*), but the rename pipeline only operates on code/locales/OpenAPI — not on push.yml strings. So a hard-coded `your.bundle.identifier` here silently desyncs from the renamed app's actual bundle id and push delivery breaks with a non-obvious error. Move all three to Rails.application.credentials.dig(:action_push_native, ...) so they're deploy-time configuration, not source-controlled state. Add the same fields to the credentials.yml.tt template so `bin/rails credentials:edit` exposes the expected key paths. Tests + rubocop clean. * openapi.yaml: document Device endpoints + schemas Layer 1 of the agent's reviewer (per the agent's docs/SPEC.md) checks OpenAPI parity between Rails ↔ iOS networking ↔ Android repository layers. Adding the Device controller without the corresponding spec entries means PRs #3-5 (the iOS/Android push registration clients) wouldn't have a contract to integrate against and would fail Layer 1 contract-parity scan. Adds: - Tag: Devices - Path POST /devices: idempotent register; 201 on create, 200 on touch, 422 on validation error - Path DELETE /devices/{deviceId}: 204 no_content, 404 if device isn't owned by current_shopkeeper - Schemas: DeviceAttributes, Device, DeviceCreateRequest (jsonapi-style envelope to match the rest of the API) YAML parses; paths now 25, schemas now 38. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
## Rename safetysection toCONTRIBUTING.mdthat links to the canonicalSUBSTRATE-CONTRACT.mdin the agent repo.a/anbeforeShop/Shopkeeper/ItemTag) and Rails-flavored failure-mode examples (OpenAPI summary"Get an item tag"→"Get an patient"; test descriptortest "destroy deletes an item_tag"→"destroy deletes an patient").Why
The agent at
nativeapptemplate-agentmechanically renames domain words across all case forms when generating customized projects. Without a link from the substrate's natural contributor onboarding path, the contract doc only reaches contributors who already know to look in the agent repo.Mirrors the section added to the iOS substrate in NativeAppTemplate-Free-iOS#67.
Test plan
SUBSTRATE-CONTRACT.md🤖 Generated with Claude Code