Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DEPLOYMENT_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ configuration, a formula result, and key deletion. The prepared justification
text is in `MARKETPLACE_LISTING.md`; the exact branding values, scope
justifications, and recording sequence are in `OAUTH_VERIFICATION.md`.

For the proven draft-install activation sequence, custom-function registration
diagnostics, reviewer-fixture rules, and submission notes, follow
`GOOGLE_MARKETPLACE_PLAYBOOK.md`.

## 5. Install a test Editor add-on

Create a blank spreadsheet containing no customer data.
Expand Down
269 changes: 269 additions & 0 deletions GOOGLE_MARKETPLACE_PLAYBOOK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# Google Sheets add-on launch playbook

This playbook captures the production lessons from the original
`OilPriceAPI for Google Sheets™` submission so later OilPriceAPI add-ons can
reuse the proven path without repeating the same diagnostics.

Use this together with:

- `DEPLOYMENT_GUIDE.md` for the release commands and Google configuration;
- `MARKETPLACE_LISTING.md` for reviewed listing copy and assets;
- `OAUTH_VERIFICATION.md` for the current submission record;
- `PORTFOLIO.md` for product order and acquisition measurement.

## Proven sequence

Follow this order. Reordering the Google steps creates ambiguous failures and
avoidable review delays.

1. Create a standalone Apps Script project and link it to a standard,
organization-controlled Google Cloud project.
2. Deploy the exact reviewed source, run automated validation, smoke it with a
non-customer key, and create an immutable Apps Script version.
3. Configure identical functional scopes in the Apps Script manifest, Google
Auth Platform Data Access page, and Workspace Marketplace SDK.
4. Create public app-specific homepage, privacy, terms, support, and opt-out
URLs. Verify the authorized domain through the same organization-controlled
account used for the Cloud project.
5. Configure OAuth as External and move it to In production.
6. Publish and verify OAuth branding before trying to prepare Data Access
verification.
7. Add the publisher or dedicated reviewer account as a Marketplace draft
tester.
8. Install the real Marketplace draft, select **Use in this document**, refresh
the spreadsheet, and run the installed-add-on smoke.
9. Record a clean OAuth review video from that installed draft.
10. Submit OAuth Data Access verification once and preserve the receipt.
11. Wait for OAuth approval before submitting or resubmitting the Marketplace
listing when Google has explicitly required that sequence.
12. Update the Marketplace integration to the exact tested Apps Script version,
submit once, and repeat the installed-add-on smoke after publication.

## Scope contract

Every OilPriceAPI Sheets add-on currently needs only these three functional
scopes:

```text
https://www.googleapis.com/auth/spreadsheets.currentonly
https://www.googleapis.com/auth/script.external_request
https://www.googleapis.com/auth/script.container.ui
```

Google may display `userinfo.email` and `userinfo.profile` as identity defaults.
Do not add those defaults to product logic, and do not add Drive-wide access.

Use this combined Data Access justification:

> OilPriceAPI for Google Sheets uses spreadsheets.currentonly to read
> user-selected inputs and write formulas and requested market-data tables only
> in the spreadsheet where the add-on is open; it does not request Drive-wide
> access. It uses script.external_request only to send HTTPS GET requests to
> api.oilpriceapi.com for market data the user explicitly requests. Requests
> include the user's stored OilPriceAPI key and selected commodity identifiers
> or filters; general spreadsheet contents are not transferred. It uses
> script.container.ui to display the add-on menu, API-key sidebar,
> price-selection dialog, help alerts, diagnostics, and recovery actions inside
> Google Sheets. These are the narrowest scopes available: currentonly limits
> spreadsheet access to the active document, external_request is required to
> call the OilPriceAPI service, and container.ui is required for the in-sheet
> sidebar and dialogs.

## The two Google test paths are not equivalent

An Apps Script Editor add-on test deployment can prove menu, sidebar, dialog,
authorization, and API request behavior. It did not register the custom
function namespace in fresh test documents during this submission:

- sidebar key save and Fetch Latest Available Prices worked;
- formula autocomplete was absent;
- even a service-free diagnostic custom function returned `#NAME?`;
- no formula execution appeared in Apps Script logs.

That combination proves a host registration problem, not an OilPriceAPI
authentication failure.

The real Marketplace draft install registered the functions correctly only
after this sequence:

1. Add the Google account as a Marketplace draft tester.
2. Install the draft from its Marketplace page.
3. Open a new blank spreadsheet.
4. Select **Extensions → Add-ons → Manage add-ons**.
5. Open the OilPriceAPI add-on menu and select **Use in this document**.
6. Refresh the spreadsheet.

After that, autocomplete appeared and `OILPRICE_PRICE`, `OILPRICE_CODES`, and
the other functions executed normally.

Do not spend API debugging time on a `#NAME?` result when:

- the sidebar fetch works;
- formula autocomplete is absent; and
- Apps Script shows no formula invocation.

First confirm installed-add-on activation and namespace registration.

## Credential-context lesson

Sidebar handlers and custom functions can run in different Apps Script
authorization contexts. A key that works in the sidebar does not by itself
prove a formula can retrieve it.

The production runtime therefore stores:

- a primary copy in document properties; and
- a compatibility copy in the spreadsheet owner's user properties, keyed by
spreadsheet ID.

The two prototype candidates also wrote an unscoped user-property key. Current
packages intentionally ignore that value because it cannot be tied to its
original spreadsheet. Prototype testers must save the key again in every
spreadsheet they continue to use.

The spreadsheet owner should configure the key. The OilPriceAPI account email
does not need to match the Google account email. The sidebar must expose only
configured/not-configured state; never return the stored credential to HTML,
cells, diagnostics, logs, screenshots, URLs, or issue trackers.

Test all four credential states:

1. no key;
2. valid key;
3. invalid or revoked key;
4. deleted key with diagnostic state cleared.

## Avoid duplicate add-on contexts

Simultaneous Apps Script test deployments and Marketplace draft installs can
produce duplicate extension entries and make results impossible to interpret.

Before the final smoke:

1. remove obsolete Apps Script test deployments;
2. uninstall stale draft installs if necessary;
3. confirm only one OilPriceAPI add-on is installed;
4. create a new spreadsheet;
5. enable the installed draft for that document;
6. refresh before testing formulas.

Name test spreadsheets with the product, version, and path, for example:
`OPA Marketplace Draft v11 Smoke`.

## Reviewer fixture

Create one persistent synthetic reviewer user and API key per listing.

- Use a clearly synthetic, non-customer email identity.
- Suppress lifecycle email, billing, and product analytics for the fixture.
- Give it only the datasets and conservative quota needed for review.
- Use a descriptive key name with the product and date.
- Verify one valid request and one invalid-key request before recording.
- Keep the key active through review, but never commit or post it.
- Deliver it only through Google's private reviewer field or existing review
email thread.

Do not rotate or delete the fixture while a case is open unless exposure is
suspected.

## Review video checklist

The reviewer video and the public marketing video are different assets.
Keep the reviewer video Unlisted when it shows an account identifier or
unverified-app screen.

Before recording:

- enable Do Not Disturb;
- close unrelated tabs and applications;
- disable visible password-manager prompts;
- use a blank spreadsheet and synthetic key;
- set the OAuth consent language to English;
- record at 1080p or better;
- rehearse the exact flow once.

Show:

1. Marketplace installation and the complete OAuth consent flow;
2. the exact app name and every requested permission;
3. **Use in this document** activation;
4. the sidebar and masked/configured credential state;
5. successful connection and response-schema validation;
6. custom-function autocomplete and a live numeric result;
7. sidebar batch fetch writing into the active spreadsheet;
8. source, unit, timestamp, and freshness metadata.

Before upload, sample the full rendered video at frequent intervals. Remove:

- raw keys, passwords, cookies, or clipboard contents;
- browser autofill and password-manager overlays;
- notification panels;
- Terminal windows and unrelated tabs;
- macOS capture controls;
- customer or personal spreadsheet data.

Upload the reviewer copy as Unlisted and verify the URL while signed out before
submitting it.

## Google Auth submission notes

- Branding must be published and verified before Data Access verification is
enabled.
- The Verification Center may describe unverified branding as “not being shown
to users” even when the underlying fields are complete. Open Branding,
publish it, then return to Verification Center.
- The Data Access form accepts one combined justification of up to 1,000
characters and a reviewer-accessible YouTube URL.
- Confirm that Restricted scopes shows no rows.
- Save Data Access changes before returning to Verification Center.
- Submit once. Repeated submissions can trigger a cooldown and delay review.
- Preserve the exact confirmation text, date, screenshot, case ID, and review
email thread.

For the original add-on, Google confirmed receipt on July 30, 2026, said the
first Trust and Safety email should arrive within 3–5 days, and warned that the
full review can take up to 4–6 weeks.

## What can and cannot be automated

Automate:

- manifest and scope parity;
- packaging and secret scans;
- API success and negative-path tests;
- asset dimensions;
- legal/disclosure URL checks;
- immutable version records;
- API-client attribution and activation measurement.

Keep a real-account smoke for:

- Marketplace draft tester installation;
- **Use in this document** activation;
- custom-function registration and autocomplete;
- OAuth consent presentation;
- Google review submission and receipt capture.

Google does not expose every Marketplace draft-install or custom-function
registration behavior through public Apps Script or Sheets APIs. API-written
formulas cannot bypass an unregistered custom-function namespace.

## Per-add-on evidence packet

Create a separate packet for every portfolio product:

- Cloud project ID and numeric project number;
- Apps Script project ID and immutable version;
- Git commit and validation output;
- exact three-scope comparison;
- public homepage, privacy, terms, support, and opt-out URLs;
- dedicated reviewer fixture record;
- installed-draft smoke spreadsheet;
- real listing screenshots;
- unlisted OAuth review video;
- OAuth receipt and case thread;
- Marketplace receipt and published listing URL;
- post-publication smoke and log review.

Do not reuse screenshots, detailed listing descriptions, reviewer keys, Cloud
projects, OAuth identities, or Apps Script projects across portfolio listings.
10 changes: 8 additions & 2 deletions OAUTH_VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,14 @@ and a non-customer test spreadsheet and OilPriceAPI key.
11. Choose Delete API Key and show that the stored-key and diagnostic states
are cleared.

The recording must not expose an API key, Google account identifier, customer
data, browser password manager, clipboard contents, or unrelated tabs.
The recording must not expose an API key, customer data, browser password
manager, clipboard contents, or unrelated tabs. Use a dedicated demo Google
account because its identifier can appear as part of the required consent
screen, and keep that reviewer copy Unlisted.

The completed reviewer video is `https://youtu.be/FakNSmBddhE`. Keep it
Unlisted. Create a different sanitized public acquisition video using
`YOUTUBE_PROMOTION.md`.

## Future release order

Expand Down
32 changes: 29 additions & 3 deletions PORTFOLIO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This repository contains five distinct Google Workspace Marketplace candidates.
They share a security and release runtime, but each product creates a different
finished workbook for a different buyer and workflow.

All five now have immutable Apps Script 1.0.0 candidates. See
`PORTFOLIO_SUBMISSION_READINESS.md` for the exact Script IDs, versions,
remaining Google Console evidence, and hold point.

| Product | Primary buyer | Activated outcome | Acquisition keyword wedge |
| --- | --- | --- | --- |
| Crack Spread Lab by OilPriceAPI | Refinery analyst or energy trader | Live refinery-margin workbook with history and sensitivity | crack spread spreadsheet |
Expand All @@ -29,6 +33,8 @@ Every release package must pass `npm run validate`.
Editor add-on smoke test and its OAuth configuration is In production.
- The five listings must not reuse screenshots or detailed descriptions.
Shared infrastructure is acceptable; duplicate user experiences are not.
- Follow the installed-draft, OAuth, reviewer-fixture, and evidence sequence in
`GOOGLE_MARKETPLACE_PLAYBOOK.md` for every candidate.

## Measurement contract

Expand All @@ -37,13 +43,16 @@ views**, segmented by product.

1. Listing discovery and install counts come from Marketplace SDK Analytics.
2. A successful product build makes an authenticated OilPriceAPI request with
`X-OilPriceAPI-Client: <product-id>/<version>`. API logs can therefore count
`X-API-Client: <product-id>/<version>`. API logs can therefore count
first data activation without collecting spreadsheet contents.
3. Sidebar signup links use:
`utm_source=workspace_marketplace`,
`utm_medium=addon`, and a unique `utm_campaign`.
4. Signup and paid conversion are joined to that campaign in the existing
first-party attribution pipeline.
5. Public tutorial links use the YouTube UTM contract in
`YOUTUBE_PROMOTION.md`, allowing YouTube sessions, signups, and first
activations to be compared with Marketplace acquisition.

The add-ons do not send spreadsheet contents, formulas, cell values, Google
account identifiers, or API keys for analytics.
Expand All @@ -58,6 +67,17 @@ npm run portfolio:verify
npm run validate
```

Account-independent release verification:

```bash
npm run test:portfolio:live
npm run portfolio:verify:remote
npm run portfolio:verify:links
```

The live smoke requires `OILPRICEAPI_KEY` to be set to a synthetic non-customer
test credential. It never prints the credential.

Deployable Apps Script roots are written to `portfolio/dist/<product-id>/`.
Run clasp from the selected product directory so its `.claspignore` exposes only
`Code.gs`, `Sidebar.html`, and `appsscript.json`.
Expand All @@ -71,5 +91,11 @@ Run clasp from the selected product directory so its `.claspignore` exposes only
5. Gas Spread Monitor

The first two have the clearest non-overlapping search intent and quickest
time-to-value. Start their test deployments first, measure activation, then
promote the strongest funnel before submitting the next Marketplace listing.
time-to-value. Their prior immutable versions are superseded; cut and remotely
verify new versions from the exact merged source before preparing the
Cloud/OAuth/Marketplace draft for Crack Spread Lab first, measure activation,
then promote the strongest funnel before submitting the next listing.

Do not submit all five simultaneously. Complete the original add-on's OAuth
review, then take Crack Spread Lab through the full installed-draft smoke and
evidence packet before cloning the operational sequence for the next product.
Loading