R-717: after_setup open_command — the family window reopens an app's DB-held sign-up switch

The command form ran only when the lock was SET, so a database switch closed by
`command` stayed closed through the household's 15-minute window. New twin
fields `open_command` + `open_success` (same service/user/args_env and the same
argv-safe expansion and success-marker rules as `command`/`success`).

- liftNativeLock (the window, via OpenSignupWindow → goNativeLock(false)): marks
  the gate record native_lock "opening" BEFORE anything opens, lifts the env,
  runs open_command; only full success records "lifted". A failed open closes
  the switch again at once and records after_setup {ok: false, step: open}; the
  app page shows its own line (app_info.signup_native_open_failed).
- The close: reconcileSignupBlocks (every 20 s and at controller start) runs
  `command` for any non-"applied" state once no window runs. A failed close
  after a window is logged ERROR ("may still be OPEN past the household's
  window") and retried every nativeLockOpenRetry (2 min) instead of 30.
- After a successful app update, verifyAndConclude → markNativeLockForReapply
  sets native_lock "" so the loop closes the switch again.
- A template with `command` but no `open_command` keeps today's window (env
  only) and logs once per app that its own switch cannot be reopened.

Tests: internal/stacks/after_setup_r717_test.go (7), web render + parity case.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-06 15:12:14 +02:00
parent 9c945688c0
commit 5b8656974b
13 changed files with 1080 additions and 39 deletions
+1
View File
@@ -2582,6 +2582,7 @@
"app_info.close_signup_text": "Anyone who finds this app's address can sign up. Close sign-up now — you can still add your family afterwards.",
"app_info.close_signup_btn": "Close sign-up now",
"app_info.signup_native_failed": "The app's own sign-up switch could not be closed. The address block still protects it.",
"app_info.signup_native_open_failed": "The app's own sign-up switch could not be opened for the 15 minutes, so it stays closed. A new family member cannot sign up in the app itself. Try again later.",
"app_info.signup_window_restart": "The app restarts for this, and once more when the 15 minutes end.",
"app_info.restarts_once": "For this the app restarts once and is briefly unavailable meanwhile.",
"err.setup_gate.close_signup_not_offered": "There is nothing to close on this app.",
+1
View File
@@ -2570,6 +2570,7 @@
"app_info.close_signup_text": "Ennél az alkalmazásnál bárki regisztrálhat, aki megtalálja a címét. Zárd le a regisztrációt most — a családtagjaidat utána is fel tudod venni.",
"app_info.close_signup_btn": "Regisztráció lezárása most",
"app_info.signup_native_failed": "Az alkalmazás saját regisztrációs kapcsolóját nem sikerült lezárni. A cím zárolása így is véd.",
"app_info.signup_native_open_failed": "Az alkalmazás saját regisztrációs kapcsolóját nem sikerült megnyitni a 15 percre, ezért zárva maradt. Új családtag így az alkalmazásban nem tud regisztrálni. Próbáld újra később.",
"app_info.signup_window_restart": "Ehhez az alkalmazás újraindul, és a 15 perc végén még egyszer.",
"app_info.restarts_once": "Ehhez az alkalmazás egyszer újraindul, és közben rövid ideig nem érhető el.",
"err.setup_gate.close_signup_not_offered": "Ennél az alkalmazásnál nincs mit lezárni.",