Skip to content

feat(gsc): submit sitemaps behind --allow-write (v0.2.1) - #14

Merged
Deniffer001 merged 2 commits into
mainfrom
feat/gsc-sitemaps-submit
Oct 21, 2026
Merged

Deniffer001 merged 2 commits into
mainfrom
feat/gsc-sitemaps-submit

Conversation

@Deniffer001

Copy link
Copy Markdown
Collaborator

Summary

Adds the first write capability to gkit: gsc.sitemaps.submit (Search Console PUT /webmasters/v3/sites/{siteUrl}/sitemaps/{feedpath}, operationId sitemaps.submit), and releases it as v0.2.1.

  • Policy / generated surface: sitemaps.submit moves from inventory to executable with effects: ["write"]; input is feedpath (required, absolute http(s) URL, maxLength 2048 like sitemaps.get) and optional siteUrl (profile default), additionalProperties: false. Manifest, inventory, and docs regenerated with generate:gsc.
  • --allow-write is now wired end to end. Before this change gsc api call rejected --allow-write as an unknown flag and the read-provider path hard-failed any non-read effect, so the gate in effects.ts was unreachable. Now args.ts parses --allow-write for gsc only, and execute-read-provider.ts runs evaluateEffects after profile selection and before dry-run, secret resolution, token derivation, artifact reservation, or dispatch. A denial returns EFFECT_NOT_ALLOWED with details.reason: "missing_allow_write", outcome: not_dispatched, and no request is sent. Same as the DataForSEO spend gate, --dry-run also needs the flag.
  • OAuth scope: the existing token used webmasters.readonly, which would 403 on every submit. Write capabilities now request https://www.googleapis.com/auth/webmasters. Reads keep the read-only scope.
  • Empty-body success: for sitemaps.submit, any 2xx (Google returns 200/204 with an empty body) skips JSON parsing. The artifact is a deterministic receipt (artifactFormat: gkit-gsc-sitemap-submit-receipt) with sorted keys and no timestamps: {feedpath, httpStatus, operation, provider, siteUrl, submitted: true}.
  • Errors: non-2xx keeps the existing mapping (401/403 → AUTH_FAILED, 429 → RATE_LIMITED, 408/5xx → UNKNOWN_OUTCOME, else PROVIDER_ERROR). A 403 on submit returns a message saying the service account lacks write permission (a Restricted user cannot submit sitemaps; grant Full/Owner), plus details.requiredPermissionLevel: "siteFullUser".
  • Ledger: not used. The only ledger in the repo is the DataForSEO spend ledger (cost policy, max-cost micros, settlement). This write has no spend and Google's PUT is idempotent (a resubmit only re-queues the sitemap). The Operation Journal ADRs are not implemented, and the complexity guardrail keeps them out of scope.

Tests

  • src/execute-gsc.test.ts (new): the gate denies both the live run and the dry-run without --allow-write, with no fetch, no token derivation, and no artifact. A dry-run with --allow-write plans a PUT. A live 204 calls deriveAccessToken(..., "write"), sends a PUT to the encoded URL, and writes the receipt artifact without leaking the token. A Restricted-user 403 maps to AUTH_FAILED with the permission message. Reads still use the read scope.
  • src/providers/gsc.test.ts: checks the PUT method, the double-encoded siteUrl/feedpath path (including ?/&), that no body or content-type is sent, and the exact receipt bytes for empty 200 and 204. Also covers 403 details, a non-HTTP feedpath rejected before dispatch, and the existing mapping for 401/429/503/400.
  • src/cli.test.ts: real argv → gate in a subprocess with a throwing fetch (denial), then --allow-write --dry-run → PUT plan.
  • src/args.test.ts: --allow-write is parsed for gsc and rejected for bing/hubspot/dataforseo.
  • scripts/generate-contract-provider.test.ts: GSC capability count 5 → 6.

Local: check-types, test (207 + site snapshot), eval, verify:package (gkit-0.2.1.tgz), and every generate:*:check all pass. No real Google calls were made.

Release

packages/gkit/package.json 0.2.0 → 0.2.1. Merging triggers Release gkit.

Expose gsc.sitemaps.submit (PUT sitemaps/{feedpath}) as the first write capability.

- Parse --allow-write for gsc api call and run the shared effect gate in the
  read-provider path before dry-run, credentials, or dispatch; denials return
  EFFECT_NOT_ALLOWED with details.reason missing_allow_write.
- Request the webmasters scope only for write capabilities; reads keep
  webmasters.readonly.
- Treat a 2xx empty body as success and publish a deterministic receipt
  (siteUrl, feedpath, httpStatus); a 403 explains that a Restricted user lacks
  write permission.
- Regenerate the GSC manifest, inventory, and docs from the reviewed policy.
@Deniffer001
Deniffer001 merged commit bc604d5 into main Oct 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant