Skip to content

fix(settings): let clear-app-state take its app from --app and refuse two apps - #3211

Merged
thymikee merged 1 commit into
mainfrom
feat/settings-permission-explicit-app
Oct 5, 2026
Merged

thymikee merged 1 commit into
mainfrom
feat/settings-permission-explicit-app

Conversation

@thymikee

@thymikee thymikee commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

Summary

#3205 landed the explicit app / --app target for settings permission and iOS settings location on|off, including the scope table and the setting_app_not_consumed refusal. This PR is now reduced to the one gap left on top of it: settings clear-app-state ignored --app.

On main, settings clear-app-state --app com.example.app (or settings clear-app-state clear --app com.example.app) dropped the named app, so the clear fell through to the app bound to the session. That is a destructive change to an app the caller never named. A positional app plus a different --app also silently kept the positional.

Now clear-app-state takes its app from the positional or from --app. A positional and a different --app are refused as a contradiction:

$ agent-device settings clear-app-state --app com.example.app      # clears com.example.app
$ agent-device settings clear-app-state com.a --app com.b
INVALID_ARGS: settings clear-app-state names two apps: com.a and com.b. Name one.

The env and config default for targetApp is still stripped for settings by the parser (#3205), so only a typed --app reaches this rule. Help text and the command reference are updated.

Not carried over from the earlier version of this PR

Main settled these differently in #3205, so they were dropped instead of being layered on:

  • Excluding targetApp from env and config for every command (main strips it for settings only, so doctor keeps its env default).
  • Moving the app onto flags.targetApp (main sends it as request input).
  • Refusing an empty app and refusing an app on reads and on unclassified settings such as wifi (main treats an empty app as no app, and leaves unclassified settings to the owner).
  • Recording --app in .ad scripts. This needs the recorder and replay to carry the request input, which is a separate change.

Validation

pnpm check:affected --base origin/main --run: format, lint, typecheck, layering, fallow, build, and command-docs pass. In the related tests, only remote-proxy-parity.test.ts > lease expiry tears the session down… failed. It is a provider proxy test that this diff does not touch, and it fails at the same point when run by itself.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 19 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/command-registry/src/flag-definitions-target.ts Outdated
Comment thread src/commands/capture/settings.ts Outdated
Comment thread scripts/integration-progress-model.ts Outdated
Comment thread packages/contracts/src/settings.ts Outdated
Comment thread packages/contracts/src/settings.ts Outdated
Comment thread packages/contracts/src/client-settings.ts Outdated
Comment thread packages/ad-script/src/internal/script.ts Outdated
Comment thread packages/ad-script/src/internal/script.ts Outdated
Comment thread packages/contracts/src/settings.test.ts Outdated
Comment thread packages/contracts/src/client-settings.ts Outdated
@thymikee

thymikee commented Oct 4, 2026

Copy link
Copy Markdown
Member Author

The code looks sound at 926a093, and I found nothing that blocks it. One open thread still needs a fix, and the branch has merge conflicts with main. The 6 checks are green.

Please change the retry hint at contracts/settings.ts:252 to use --app ${app}. Settings positionals are setting, state, target and mode, so settings permission grant location ${app} parses the app as the mode. Then rebase onto main. The other open thread that still applies is the one about the integration row text overclaiming (r4178279223), plus seven lower-priority threads that still apply.

Not blocking, and you can take or leave these: adding targetApp to ENV_EXCLUDED_FLAG_KEYS also retires the AGENT_DEVICE_TARGET_APP default for doctor, so a line in the PR body or release notes would help; the clear-app-state writer copies the positional app onto --app, and the reader drops a mismatched --app silently (settings clear-app-state clear com.a --app com.b); and SETTINGS_FLAGS_BY_ACTION repeats part of settingsAppScope, with long narration comments in several files.

Could the CLI admit --app through allowedFlags only, and let the daemon refuse the wifi, airplane and read legs with its typed reason? The scope table in contracts would stay the single decision. That would drop SETTINGS_FLAGS_BY_ACTION and SETTINGS_CLI_FLAGS. The two refusal builders could share one return type. Together with trimmed comments, that looks like about 150-180 production lines with the same behavior. Nothing has to change first, since the table already exists.

These threads do not apply, so you can resolve them: r4178279217 (doctor never records an action, so only settings carries targetApp); r4178279219 (location set --app is refused daemon-side with setting_app_not_consumed, and the CLI admission is a deliberate backstop); r4178279233 (physical Apple devices are refused at admission, since setSetting is declared simulator-only); r4178279243 (the same docs bullet already lists the macOS host permissions as refused).

I did not run tests. The PR body mentions a live iOS simulator run, but I saw no transcript, so I could not confirm it. The Android pm path with a named app has unit tests only.

@thymikee
thymikee force-pushed the feat/settings-permission-explicit-app branch from 926a093 to 600cda7 Compare October 5, 2026 06:21
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-05 11:02 UTC

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.06 MB 5.06 MB +273 B
Package (unpacked) 5.06 MB 5.06 MB +273 B
Package (download) 1.52 MB 1.52 MB +71 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.0 ms 27.1 ms +0.1 ms
CLI --help 83.7 ms 82.6 ms -1.1 ms

@thymikee

thymikee commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

Since 926a093, the new head 600cda7 fixes the earlier code findings, but live evidence is still missing. The flagsByAction table is gone, location set --app is now refused daemon-side, the hint says --app, the macOS docs are qualified, and the .ad writer and reader handle a second --app safely. The code looks right to me. I ran no tests locally, so my read is from the diff only.

The Coverage job fails in eager-closure-budgets for packages/contracts/src/settings.ts. This PR adds that edge: https://github.com/callstack/agent-device/blob/600cda7/packages/contracts/src/settings.ts#L1 now imports @agent-device/kernel/device, only for isIosFamily and isMacOs in the scope table. The only production consumer of settingsAppScope is src/daemon/handlers/snapshot-settings.ts, which already imports kernel/device. Please keep contracts/settings.ts free of kernel/device edges and move settingsAppScope, its type and its scope-table tests next to that consumer in src/daemon. The refusal builders can stay in contracts. Please do not raise the budget.

The live iOS run in the PR body was on 926a093 and has no transcript. Android was never run live. Please run against 600cda7 with no session app open and paste the transcript. On the iOS simulator, show these.

  • settings permission grant camera --app <bundle> succeeds, and xcrun simctl privacy or the app shows the grant.
  • settings location on --app <bundle> succeeds.
  • settings wifi on --app <bundle> and settings location set 1 2 --app <bundle> return the daemon's INVALID_ARGS with details.reason set to setting_app_not_consumed and dispatched no, not a parser error.

On the Android emulator, show these.

  • settings permission grant camera --app <package> succeeds, and adb shell dumpsys package <package> shows CAMERA granted.
  • settings location on --app <package> refuses with setting_app_not_consumed.

The "(parser)" refusal claim for wifi in the PR body is stale, because the refusal is now daemon-side. Only the body text is out of date.

Of the open inline threads, these do not apply and can be resolved.

No open thread still applies.

Before merge, move settingsAppScope into the daemon so Coverage turns green. Then post the iOS and Android transcripts from 600cda7.

@thymikee

thymikee commented Oct 5, 2026 •

Copy link
Copy Markdown
Member Author

Rebased onto main (db56bb77d) and pushed 600cda745. Both blocking asks are in: the retry hint now reads settings permission grant location --app <app>, and the branch is rebased (it had picked up the session-lifetime and session_app_required changes; the handler tests moved from set to publish).

Of the non-blocking list:

  • the PR body now has a Behavior change worth a release note section covering the AGENT_DEVICE_TARGET_APP default retiring for doctor as well;
  • settings clear-app-state clear com.a --app com.b no longer drops one silently — it refuses with "names two apps";
  • SETTINGS_FLAGS_BY_ACTION and SETTINGS_CLI_FLAGS are gone. The CLI admits --app through allowedFlags only, the daemon refuses the wifi/airplane/read legs with setting_app_not_consumed, and settingsAppScope is the single decision;
  • the two refusal builders share SettingsAppRefusal;
  • the narration comments on the writer, the reader base, and the scope table are trimmed.

Behavioral consequence worth flagging: dropping flagsByAction means --app no longer fails at the parser for a device-wide leg. settings wifi on --app com.example.app now reaches the daemon and refuses there with the typed reason, which is strictly more informative than "wifi does not read --app" — it names the app that was refused and points at an app-scoped alternative. The parser test asserts the acceptance side; the handler suite asserts the refusal side.

Nine of thirteen threads were fixed in that commit; the other four are resolved with the reason from your read (doctor never records an action, the location set CLI admission is a backstop, physical Apple targets stop at admission, and the macOS refusal was already in the bullet). The PR body carries the live simulator transcript, including the new refusals and the env-default check.

@thymikee

thymikee commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

Coverage failed on 600cda745 at the eager-closure gate, not on a test: importing isIosFamily/isMacOs from @agent-device/kernel/device into packages/contracts/src/settings.ts grew that module's eager closure from 3 modules to 5. It is a vocabulary facade the public client imports, so the device model must not be reachable from it — ADR 0019's loading shape, which check:affected does not run.

Fixed in c4f9ca626 without moving the table. settingsAppScope now keys on a SettingsTargetFamily (ios-family | macos | other) and the contracts module is back to its merge-base import list; the daemon maps the device to the family through settingsTargetFamily(), which is where the kernel predicates stay single-sourced. So the point from thread r4178279254 holds — isIosFamily is the one predicate — it is just called at the daemon seam rather than inside the vocabulary module.

Verified: settings/__tests__/eager-closure-budgets.test.ts passes against the committed tree (759 tests), check:affected --base origin/main --run green, and live on the simulator permission grant and location on still take a named app with no session while wifi on --app still refuses with setting_app_not_consumed.

… two apps (#3179)

`settings clear-app-state --app com.example.app` dropped the named app: the CLI only folded
`--app` into the settings whose app has no positional, so the clear fell through to the session
app, a destructive change to an app the caller never named. A positional app plus a different
`--app` silently kept the positional.

The clear-app-state parse now resolves its app from the positional or `--app`, and refuses a
positional and a different `--app` as a contradiction instead of picking one.
@thymikee
thymikee force-pushed the feat/settings-permission-explicit-app branch from c4f9ca6 to 4085ad6 Compare October 5, 2026 09:21
@thymikee thymikee changed the title feat(settings): let an app-scoped change name its app fix(settings): let clear-app-state take its app from --app and refuse two apps Oct 5, 2026
@thymikee

thymikee commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

The one Smoke Tests failure on 4085ad6 is a transport flake, not this diff: a daemon_request_timeout (90s) on step cold launch fixture through a deep link (open … --relaunch --launch-url …), with daemonPreservedAfterTimeout: true and the liveness probe answering — the open leg never completed while the daemon stayed up. The diff is the settings CLI reader only, which the open path never loads, and the other three Smoke matrix cells passed. Rerunning the failed job.

@thymikee

thymikee commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

Reviewed at 4085ad6: the code looks good to merge. The open question from the 600cda7 review is settled, because the change only maps argv to input and sends the same positionals as the existing route, so it needs no live run.

Not blocking, take or leave: the two-app test in src/commands/capture/settings.test.ts checks only the message, so could it also assert the INVALID_ARGS code? And does the parser accept --app ''? If it does, an empty app would fall through to the session app on the daemon side, so readClearAppStateApp could refuse it as INVALID_ARGS.

The one failing Smoke cell fails in "Preflight iOS runner through public CLI" with daemon_startup_failed before any settings command runs, so it looks unrelated to this diff. Your note describes a deep-link daemon_request_timeout, which is a different failure, so please check the rerun result. There are no conflicts.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 5, 2026
@thymikee
thymikee merged commit df7ad0c into main Oct 5, 2026
20 of 22 checks passed
@thymikee
thymikee deleted the feat/settings-permission-explicit-app branch October 5, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant