# Guide for coding agents

This page is written to you, the coding agent asked to integrate SocialScope into an existing application. It tells you what to read, what to inspect in the app first, the rules you must not break, the order to build things in, what to test and what to report. SocialScope gives the app hosted OAuth for YouTube, TikTok and Instagram plus observed account data over one server-to-server GraphQL API, and optionally Google sign-in. The app keeps its own backend, users, sessions and database. No SDK or npm package is needed.

## 1. Load the contract

Read these before writing code. They are plain Markdown.

| Need                      | URL                                                                |
| ------------------------- | ------------------------------------------------------------------ |
| Index of every page       | `https://docs.socialscope.hardscope.incdev.dev/llms.txt`           |
| The 5-minute path         | `https://docs.socialscope.hardscope.incdev.dev/quickstart.md`      |
| Connect flow and outcomes | `https://docs.socialscope.hardscope.incdev.dev/connect.md`         |
| Reading data              | `https://docs.socialscope.hardscope.incdev.dev/reading-data.md`    |
| Google sign-in            | `https://docs.socialscope.hardscope.incdev.dev/google-identity.md` |
| Every error code          | `https://docs.socialscope.hardscope.incdev.dev/errors.md`          |
| Every operation           | `https://docs.socialscope.hardscope.incdev.dev/api-reference.md`   |
| GraphQL schema            | `https://docs.socialscope.hardscope.incdev.dev/schema.graphql`     |

Use only operations and fields that exist in the schema. If you cannot fetch the docs or the schema, finish the inventory below and report that blocker. Do not invent operations or fields.

Validate every GraphQL document you write against `/schema.graphql` locally, for example with `graphql-js` `buildSchema` and `validate` in a unit test. SocialScope answers an invalid query with a bare `BAD_USER_INPUT` that does not name the bad field.

## 2. Inventory the app before designing

Find and write down, with file paths:

- Server routes or server actions, and how the app protects them against CSRF.
- How a signed-in user is identified on the server, and the stable user ID to use as `externalSubjectId`.
- How the current workspace is selected and authorized, to map to `tenantKey`. If the app has no workspaces, it uses one fixed tenant key from server configuration.
- The secret store and how server environment variables are read.
- The existing HTTP or API client conventions, logging and error reporting.
- The database and migration tool, for the attempt records you need to store.
- The test framework and how routes are tested.

Reuse all of these. Do not add a new auth system, database, queue or framework for this integration.

Then pick the flows the task asks for: social account connection, Google sign-in, or both. They are separate. Google sign-in grants no YouTube access, and connecting YouTube signs nobody in.

## 3. Rules

You must follow every rule in this list.

### Secrets

1. Keep the application key server-side only, in the app's secret store, as `SOCIALSCOPE_CONSUMER_KEY`, next to `SOCIALSCOPE_GRAPHQL_URL`.
2. Never put the key in `NEXT_PUBLIC_*`, `VITE_*` or any client-bundled variable, browser storage, a mobile bundle, source control, test fixtures or logs.
3. Never log the `Authorization` header, a `launchToken`, a `resultProof` or raw SocialScope responses.
4. Use only the key issued to your app.

### Ownership

5. Derive `tenantKey` and `externalSubjectId` from the server session on every call. Never accept them from a form field, query string, header or request body the browser controls.
6. The key identifies the app. There is no app ID argument. Never pass or trust an app ID from the browser.
7. Connections are separate per app, tenant and subject. Do not try to share them between apps.

### Connect

8. Call `integrationReadiness` before offering a provider. Show the action as unavailable when the provider is `false` or the call fails. Never bypass readiness.
9. Create the attempt with `createConnectSession` in an authenticated, CSRF-protected server action, and save its `id` with the user, tenant and provider before sending the browser anywhere.
10. Check `launchUrl` is on the Connect host (the origin of `SOCIALSCOPE_GRAPHQL_URL`) with path exactly `/api/connect/redeem` and no query or fragment.
11. Serve a no-store HTML page from the origin of the return URL that auto-submits a top-level form posting only `launchToken`. Escape both values. Send `Referrer-Policy: strict-origin`. Never put the token in a URL and never send it from the server.
12. On return, treat `ss_session_id` as a hint only. Require the same signed-in user and tenant, match an unused saved attempt, mark it used, then call `connectSessionResult` with the saved scope and ID.
13. Show success only when the result is `COMPLETED`, its IDs match, and `connection.status` is `CONNECTED`. Present posts by `syncStatus`: `PENDING` or `RUNNING` is loading, `READY` is loaded, `PARTIAL` is loaded but possibly incomplete, `STALE` shows the stored data with its age, `FAILED` is an error with a retry.
14. Show the "remove the app in the provider's settings" instruction whenever `creatorAction` is set, including values you do not recognize.
15. On denial, expiry, failure or SocialScope downtime, offer a retry. Never infer success and never fall back to the app's own provider OAuth.
16. Remove `ss_session_id` and `ss_identity_id` from the URL before analytics or third-party scripts load.
17. The session cookie checked on the return routes must be `SameSite=Lax` or `None`, because SocialScope reaches them with a cross-site redirect.
18. In production, `FORBIDDEN` from `createConnectSession` and `false` readiness can be transient while a YouTube or TikTok disconnect settles. Retry later before treating it as misconfiguration.

### Data

19. Never show an unavailable metric as `0`. Only `availability: AVAILABLE` carries a value.
20. Treat `DecimalCount` as an integer string. Do not parse it into a float.
21. Show `observedAt` next to counts. Keep `sourceField` when you interpret them.
22. Do not claim a list is complete unless `pageInfo.completeForWindow` is `true` for that exact window.
23. Refresh and lookup are asynchronous and there are no webhooks. Poll `syncJob` before reading again. After a lookup succeeds, read `contentItem` and check its `availability`.
24. Store the connection IDs you receive in your own database and read each with `connection`. A `CONNECTED` account can still fail its read with `AUTHORIZATION_SUSPENDED`, `RECONNECT_REQUIRED`, `CAPABILITY_UNAVAILABLE` or `FORBIDDEN`, and one such account fails the whole `connections` list. Map each code to that account's state.
25. Do not tell a person a disconnect revoked access at the provider unless the revoke receipt is `SUCCEEDED` with a null `creatorAction`. Instagram never confirms a revoke.

### Google identity

26. Bind `createGoogleIdentitySession` to the browser session. Do not require an existing local user.
27. Keep `resultProof` on the server. Redeem once, with the saved tenant and proof, and only for the browser session that started the attempt.
28. Create or find the local user only on `COMPLETED` with non-empty `issuer` and `googleSub`, keyed by that pair. Never link accounts by email. Check `emailVerified`.
29. Create the app's session only after the user write succeeds.

### Scope of your work

30. Registering the app, issuing keys and enabling providers or Google identity are done by a SocialScope admin. List what the admin needs. Do not attempt it yourself.
31. A disabled readiness value is not permission to widen access or work around it.

## 4. Build order

1. Add the server-only helper from [Authentication](/authentication.md) and the two environment variables.
2. Add the readiness check and render provider actions from it.
3. Add the attempt store: a table or existing session store holding attempt ID, user ID, tenant key, provider, `resultExpiresAt` and a used flag.
4. Add the start action and the launch page from [Connect](/connect.md).
5. Add the return route with the full verification from [Connect](/connect.md#4-verify-the-return).
6. Add the reads the feature needs from [Reading data](/reading-data.md), with the availability and completeness rules.
7. Add reconnect and disconnect if the feature needs them.
8. Add Google identity only if asked, following [Google identity](/google-identity.md).
9. Write the tests below as you go, one behavior at a time.

## 5. Tests to write

Use the app's own test tools. Stub SocialScope at the HTTP boundary with responses shaped like the schema. See [Testing](/testing.md) for an example.

- Two users: neither can read the other's connections.
- Two app keys: the second app cannot read the first app's connections.
- A foreign, duplicate, malformed or used `ss_session_id` is rejected without showing success.
- An unavailable provider in readiness renders as unavailable. A failed readiness call does too.
- Provider denial and expiry produce a retry.
- `COMPLETED` with a connection that is not `CONNECTED` is not shown as connected.
- A metric that is not `AVAILABLE` renders as "not available", not `0`.
- For Google identity: a new user, a returning user, and a rejected second redemption.
- The key does not appear in client bundles.

A stubbed suite and SocialScope's local fixture prove wiring, not provider approval or a live consent.

## 6. Report back

When you finish, report:

- The routes and files you added or changed.
- The admin registration the app needs, without secret values: slug, exact origins, return URLs, tenant keys, providers, capabilities, and the Google identity return URL if used.
- The server settings to add: `SOCIALSCOPE_GRAPHQL_URL` and `SOCIALSCOPE_CONSUMER_KEY`, with no values.
- The checks you ran and their results.
- What still needs a live check: one real account connected per provider on the target environment, and a real Google sign-in if used.
- Any part of the contract you could not read, and the date you read the docs.
