Console pages as absolute deep links: cli/lib/console-links.ts builds the URL of every App Store Connect / Play Console page from apple.appId, play.developerId and play.appId (app list / developer chooser when unknown); every plain 'App Store Connect > ...' / 'Play Console > ...' direction in cli/ and templates/ now carries it, validate / scan / declarations / release plan JSON carry url, the desktop shows an Open button; tests/console-links.test.ts guards it; AGENTS rule 11, pitfall 71

This commit is contained in:
CRTkafa 2026-10-15 19:55:37 +08:00
parent f898d642d0
commit e500e88e40
50 changed files with 381 additions and 92 deletions

View file

@ -166,7 +166,7 @@ function privacyPolicy(config: ShipConfig, p: PlanIds): Part {
title: 'Play Console: Privacy policy (App content)',
start: start(config, p, 'app-content/privacy-policy', appContentReach('Privacy policy')),
steps: steps([
{ text: 'Open App content > Privacy policy (/app-content/privacy-policy).' },
{ text: `Open App content > Privacy policy: ${start(config, p, 'app-content/privacy-policy', []).url}` },
{ text: 'Type the privacy policy URL into the Privacy policy URL field (replace what is there).', answers: ['url'] },
{ text: DECLARATION_SAVE },
{ text: 'Verify: read the page text and pipe it to `storebound console verify play-privacy-policy --from-text -`.' },
@ -176,7 +176,7 @@ function privacyPolicy(config: ShipConfig, p: PlanIds): Part {
...declarationButtons('Privacy policy'),
uiFacts: ['Verified 2026-10-12: the card\'s page is /app-content/privacy-policy (one URL field).', 'The URL must be public (no PDF, no sign-in): `storebound validate` checks it.', PLAY_MATERIAL_FACT],
verify: pageVerify('play-privacy-policy', ['The field shows app.privacyPolicyUrl.']),
guidance: ['Open: Policy and programs > App content > Privacy policy'],
guidance: [`Open: Policy and programs > App content > Privacy policy: ${start(config, p, 'app-content/privacy-policy', []).url}`],
};
}
@ -195,7 +195,7 @@ function adsDeclaration(config: ShipConfig, p: PlanIds): Part {
title: 'Play Console: Ads (App content)',
start: start(config, p, 'app-content/ads-declaration', appContentReach('Ads')),
steps: steps([
{ text: 'Open App content > Ads (/app-content/ads-declaration).' },
{ text: `Open App content > Ads: ${start(config, p, 'app-content/ads-declaration', []).url}` },
{ text: 'Pick the radio (JS click on the inner [role=radio]); read aria-checked back.', answers: ['ads'] },
{ text: DECLARATION_SAVE },
{ text: 'Verify: read the page text and pipe it to `storebound console verify play-ads --from-text -`.' },
@ -205,7 +205,7 @@ function adsDeclaration(config: ShipConfig, p: PlanIds): Part {
...declarationButtons('Ads'),
uiFacts: ['Verified 2026-10-12: /app-content/ads-declaration.', 'Rewarded ads count as ads.', PLAY_MATERIAL_FACT],
verify: pageVerify('play-ads', ['The selected radio matches the sheet.']),
guidance: ['Open: Policy and programs > App content > Ads'],
guidance: [`Open: Policy and programs > App content > Ads: ${start(config, p, 'app-content/ads-declaration', []).url}`],
};
}
@ -232,7 +232,7 @@ function signInDetails(config: ShipConfig, p: PlanIds): Part {
title: 'Play Console: Sign in details (App access)',
start: start(config, p, 'app-content/testing-credentials', appContentReach('Sign in details')),
steps: steps([
{ text: 'Open App content > Sign in details (/app-content/testing-credentials).' },
{ text: `Open App content > Sign in details: ${start(config, p, 'app-content/testing-credentials', []).url}` },
{ text: 'Pick whether any functionality is restricted (material radio: JS click on the inner [role=radio]).', answers: ['access'] },
ra?.signInRequired && { text: p.demoAccount
? 'Restricted: press "Add instructions", type the instructions, then type the Username and Password from .storebound/credentials.json (reviewDemoAccount; click each field and type: never echo them in chat or logs).'
@ -244,7 +244,7 @@ function signInDetails(config: ShipConfig, p: PlanIds): Part {
...declarationButtons('Sign in details', [{ button: 'Add instructions', class: 'navigation', where: 'Sign in details: opens the instructions dialog' }]),
uiFacts: ['Verified 2026-10-12: the Sign in details card (App access) is /app-content/testing-credentials.', 'Credentials are never in the config, the plan or chat: the agent types them from .storebound/credentials.json (reviewDemoAccount) without printing them; without that entry the owner types them.', PLAY_MATERIAL_FACT],
verify: pageVerify('play-sign-in-details', ['The selected radio matches reviewAccess.signInRequired; with a sign-in, one set of instructions is listed.']),
guidance: ['Open: Policy and programs > App content > Sign in details (App access)'],
guidance: [`Open: Policy and programs > App content > Sign in details (App access): ${start(config, p, 'app-content/testing-credentials', []).url}`],
};
}
@ -292,7 +292,7 @@ function advertisingId(config: ShipConfig, p: PlanIds): Part {
title: 'Play Console: Advertising ID (App content)',
start: start(config, p, 'app-content/ad-id-declaration', appContentReach('Advertising ID')),
steps: steps([
{ text: 'Open App content > Advertising ID (/app-content/ad-id-declaration).' },
{ text: `Open App content > Advertising ID: ${start(config, p, 'app-content/ad-id-declaration', []).url}` },
{ text: 'Pick Yes or No (material radio: JS click on the inner [role=radio]).', answers: ['uses'] },
uses.v === true && { text: 'Tick exactly the purposes of the sheet (material checkboxes: JS click on the inner [role=checkbox]; read aria-checked back).', answers: out.filter(a => a.id.startsWith('purpose')).map(a => a.id) },
{ text: DECLARATION_SAVE },
@ -302,7 +302,7 @@ function advertisingId(config: ShipConfig, p: PlanIds): Part {
...declarationButtons('Advertising ID'),
uiFacts: ['Verified 2026-10-12: /app-content/ad-id-declaration.', 'Yes needs the com.google.android.gms.permission.AD_ID permission in the merged manifest (ad SDKs usually add it); `storebound scan` reports the SDKs.', PLAY_MATERIAL_FACT],
verify: pageVerify('play-advertising-id', ['The selected radio and the ticked purposes match the sheet.']),
guidance: ['Open: Policy and programs > App content > Advertising ID'],
guidance: [`Open: Policy and programs > App content > Advertising ID: ${start(config, p, 'app-content/ad-id-declaration', []).url}`],
};
}
@ -312,7 +312,7 @@ function government(config: ShipConfig, p: PlanIds): Part {
title: 'Play Console: Government apps (App content)',
start: start(config, p, 'app-content/government-apps', appContentReach('Government apps')),
steps: steps([
{ text: 'Open App content > Government apps (/app-content/government-apps).' },
{ text: `Open App content > Government apps: ${start(config, p, 'app-content/government-apps', []).url}` },
{ text: 'Pick whether the app is developed by or on behalf of a government (material radio).', answers: ['government'] },
{ text: DECLARATION_SAVE },
{ text: 'Verify: read the page text and pipe it to `storebound console verify play-government-apps --from-text -`.' },
@ -322,7 +322,7 @@ function government(config: ShipConfig, p: PlanIds): Part {
...declarationButtons('Government apps'),
uiFacts: ['Verified 2026-10-12: /app-content/government-apps.', PLAY_MATERIAL_FACT],
verify: pageVerify('play-government-apps', ['The selected answer matches the sheet.']),
guidance: ['Open: Policy and programs > App content > Government apps'],
guidance: [`Open: Policy and programs > App content > Government apps: ${start(config, p, 'app-content/government-apps', []).url}`],
};
}
@ -343,7 +343,7 @@ function featureCard(config: ShipConfig, p: PlanIds, kind: 'finance' | 'health')
title: `Play Console: ${name} (App content)`,
start: start(config, p, `app-content/${kind}`, appContentReach(name)),
steps: steps([
{ text: `Open App content > ${name} (/app-content/${kind}).` },
{ text: `Open App content > ${name}: ${start(config, p, `app-content/${kind}`, []).url}` },
{ text: 'Step 1: read it and press Next.', click: ['Next'] },
{ text: `Last step: tick exactly the boxes of the sheet (material checkboxes: JS click on the inner [role=checkbox]; read aria-checked back)${fin ? '; no financial features: "My app doesn\'t provide any financial features"' : '; no health features: "Other" (as on the verified run) or the none option the page offers'}. Then Next if the page shows one.`, click: ['Next'], answers: out.map(a => a.id) },
{ text: DECLARATION_SAVE },
@ -353,7 +353,7 @@ function featureCard(config: ShipConfig, p: PlanIds, kind: 'finance' | 'health')
...declarationButtons(name, [{ button: 'Next', class: 'navigation', where: `${name}: between the two steps` }]),
uiFacts: [`Verified 2026-10-12: /app-content/${kind}, two steps; the last step holds ${fin ? '"My app doesn\'t provide any financial features"' : 'the health feature boxes, "Other" among them'}; then Next and Save.`, PLAY_MATERIAL_FACT],
verify: pageVerify(form, ['The ticked boxes match the sheet.']),
guidance: [`Open: Policy and programs > App content > ${name}`],
guidance: [`Open: Policy and programs > App content > ${name}: ${start(config, p, `app-content/${kind}`, []).url}`],
};
}