feat(android): add safe cron job management (#102997)

* feat(android): add safe cron job management

Co-authored-by: snowzlmbot <293528334+snowzlmbot@users.noreply.github.com>

* fix(android): harden cron editor state

* fix(android): preserve cron state across lifecycle

* fix(android): satisfy cron release gates

* fix(android): retain cron drafts outside saved state

* fix(android): scope cron auto-delete to one-shot jobs

* fix(android): use Compose activity owner

* docs(changelog): note Android cron management

* fix(cron): harden Android job management

* chore(i18n): refresh Android cron inventory

* test(cron): cover enriched read views

* chore(changelog): defer Android cron note to release

---------

Co-authored-by: snowzlmbot <293528334+snowzlmbot@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-07-10 04:12:18 +01:00
committed by GitHub
parent e8fcc93cd3
commit 1696366f88
36 changed files with 4470 additions and 284 deletions
@@ -357,6 +357,14 @@ function expectCronSuccess(respond: ReturnType<typeof vi.fn>): void {
expect(respond).toHaveBeenCalledWith(true, expect.objectContaining({ id: "cron-1" }), undefined);
}
function expectCronReadSuccess(respond: ReturnType<typeof vi.fn>, job: CronJob): void {
expect(respond).toHaveBeenCalledWith(
true,
expect.objectContaining({ ...job, configRevision: expect.stringMatching(/^sha256:/) }),
undefined,
);
}
function requireRecord(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new Error(`expected ${label} to be an object`);
@@ -501,7 +509,7 @@ describe("cron method validation", () => {
const { context, respond } = await invokeCronGet({ id: "cron-42" }, job);
expect(context.cron.readJob).toHaveBeenCalledWith("cron-42");
expect(respond).toHaveBeenCalledWith(true, job, undefined);
expectCronReadSuccess(respond, job);
});
it("allows caller-scoped cron.get for the same agent", async () => {
@@ -511,7 +519,7 @@ describe("cron method validation", () => {
client: callerClient("ops"),
});
expect(respond).toHaveBeenCalledWith(true, job, undefined);
expectCronReadSuccess(respond, job);
});
it("hides caller-scoped cron.get for a foreign agent", async () => {