# Contributing

Welcome! `dsh-coding-subscription-oauth` is an open-source coding-subscription OAuth plugin for DeepSeek Harness. We appreciate issue reports, questions and pull requests. Read `docs/00-project-rules.md` first — it defines the release loop, versioning, commit/push hygiene and the publish vs local-only document split that every contribution must respect.

## Code of Conduct

- Be respectful and constructive in issues, PRs and reviews.
- Only ever use coding subscriptions you own. The project does not support bulk accounts, quota resale, remote relay, paywall bypass or client impersonation — see the compliance note in `README.md`.

## Getting started

Primary verification is `pnpm run check` on Node matching `.nvmrc` (Cursor Cloud or an isolated checkout). Do not install into a shared operator DSH profile; use `export DSH_HOME=/tmp/dsh-verify-*` (or `$HOME/.dsh-cloud`) for plugin smoke tests.

Docker sandbox targets remain available for CI and for contributors who prefer a hermetic build. The tracked `Dockerfile` copies the filtered source into the image (never credentials), downloads dependencies in a dedicated stage, then runs project code with `--network=none`. Do not use privileged mode, credential or host-directory bind mounts, or the Docker socket. Tests use no published ports. The narrowly controlled interactive Web preview below is the only port-publishing exception; host networking remains prohibited unless its documented fallback conditions are all met.

```bash
pnpm install --frozen-lockfile
pnpm run check
```

Optional Docker:

```bash
docker build --target check --build-arg NODE_VERSION=22.19.0 \
  --resource memory=3g --resource cpu-quota=200000 \
  --tag test-dsh-coding-oauth:check .

docker build --target verify --build-arg NODE_VERSION=22.19.0 \
  --resource memory=3g --resource cpu-quota=200000 \
  --tag test-dsh-coding-oauth:verify .
```

The `artifacts`, `package`, `inspect`, and `isolated-install` targets cover generated `lib/`, the candidate tarball, release inspection, and a script-disabled consumer install.

### Isolated Web preview

`docker/run-preview.sh` is the only supported interactive preview launcher. It builds `web-preview` offline from two auditable inputs: this checkout's committed `lib/` and a BuildKit named context containing an installed `@deepseek-ai/dsh` **program package** (any audited version whose peer tree satisfies this plugin). Point `DSH_INSTALL_DIR` at that package directory, never at a DSH home/profile or any credential directory:

```bash
export DSH_INSTALL_DIR=/path/to/node_modules/@deepseek-ai/dsh
export DSH_PREVIEW_AUTHORITIES=review-host.example:17800
./docker/run-preview.sh
```

The launcher defaults to Docker bridge networking and `0.0.0.0:17800`, checks the allowed high-port ranges, and enforces a non-root user, read-only root filesystem, dropped capabilities, no-new-privileges, CPU/memory/PID limits, isolated named volumes, and tmpfs runtime state. The DSH backend remains on a separate loopback-only high port. It does not mount, modify, restart, or validate against an existing host DSH. If provider endpoints require an outbound proxy, set `DSH_PREVIEW_PROXY` explicitly.

The launcher prints a command for reading the runtime-generated bootstrap token from the container. Append it to the printed local or allowlisted remote URL as `?preview_token=<token>` using a private channel. The proxy exchanges it once for an HttpOnly, SameSite=Strict cookie and redirects to a token-free URL. Treat both the bootstrap URL and the isolated preview volumes as credentials; never paste either into logs, issues, commits, or chat.

Host networking is **not** a normal preview mode. It may be selected only after a minimal container proves ordinary Docker port publishing unavailable and the maintainer explicitly approves that one run. The explicit guard is:

```bash
DSH_PREVIEW_NETWORK=host \
DSH_PREVIEW_HOST_NETWORK_CONFIRMED=yes \
./docker/run-preview.sh
```

Even then, the launcher permits only the isolated high-port ranges and refuses occupied preview/backend ports. Stop the preview with the exact `docker rm -f <container>` command printed by the launcher. Remove its named volumes only after confirming they contain no OAuth credentials that need to be retained.

This repo also ships a Grok Build CLI (`dsh-coding-oauth`, legacy `dsh-grok-build`), an OAuth settings page, and verification scripts for a live deployment (`verify:deployed` / `smoke:deployed`). Those exercise real providers, so they are meant for maintainer/dev workflows, not for CI.

## Development flow

1. Open an issue describing the change (or link an existing one) so scope is agreed first.
2. Branch from the default branch. Keep commits atomic and conventional — see **Commits & pushes** below.
3. When you change a capability or add a doc, update `README.md` (and the community translations added in `docs/00-project-rules.md` §2 if user-facing) and the relevant entries in `docs/` (public layer), **and** add a changelog entry under `Unreleased` in `CHANGELOG.md`.
4. Run `pnpm run check` until green (or the Docker `check` / `verify` targets if you prefer a hermetic sandbox), then commit that passing slice promptly (do not stack later work on an uncommitted green tree).
5. Push the branch as a version/milestone checkpoint and open a PR. Describe what changed and how it was verified. Keep the scope of local-only docs (`docs/local/`) out of the PR unless you are a maintainer doing internal investigation.

## Commits & pushes

History is part of the review. The maintainer counterpart — tags, clean-tree releases, changelog folding — lives in `docs/00-project-rules.md` §7.

### Conventional, atomic commits

- Use [Conventional Commits](https://www.conventionalcommits.org/): `type(optional-scope): summary` in the imperative, about 50–72 characters.
- Types:
  - `feat:` new capability / route / provider
  - `fix:` bug fix
  - `docs:` documentation (publishable layer)
  - `test:` tests
  - `refactor:` behaviour-preserving cleanup
  - `build:` toolchain, packaging, or committed `lib/` artifacts
  - `ci:` CI workflow
  - `chore:` process-only (including a release bump)
- Optional scopes such as `M1` / `M3` or a module name are welcome when they help a reviewer.
- **One coherent concern per commit.** Do not mix docs, build/toolchain and feature/fix work unless they are inseparable (a new capability that cannot be reviewed without its README/changelog note, or a source change that must ship with the `lib/` it generated).
- Do not rewrite published history. Amend or squash only on an unpushed local commit.

### Before you commit

- Run `pnpm run check` (or the Docker `check` then `verify` targets) and wait until green. Do not commit a failing tree.
- Commit promptly once checks pass — do not leave a finished, verified change sitting uncommitted next to later work.
- Generated `lib/` is a committed release artifact (git installs + the CI `git diff --exit-code -- lib` drift gate). Rebuild it and include it in the **same** commit as the source or build-script change that produced it. Do not land stale `lib/` against newer `src/`, and do not land a `lib/`-only commit unless the only change is a verified rebuild with no source delta.
- Never commit secrets, tokens, credentials, private keys, `.env` files, host-specific paths, or local-only notes (`docs/local/`, `reference/`). See `docs/00-project-rules.md` §0.3.

### Pushing

- Push the feature branch as a **checkpoint** at each version or milestone (for example after an M1/M2/M3 slice, or when a version-ready cut is green), not only when the PR is finished.
- Never force-push (`--force` / `--force-with-lease`) without **explicit maintainer approval**. Default history is append-only, including on your own feature branch once it has been pushed.
- Open the PR from a pushed checkpoint. Describe what changed and how it was verified.

## Review & merging

- The maintainer may self-approve and merge; additional review is optional.
- A PR that changes public behaviour must not be merged without its README/changelog updates.
- Do not force-push `main` or a published release tag. Feature-branch force-pushes still need explicit approval (see above).
- Maintainers run the release loop (`docs/00-project-rules.md` §3–4 and §7) after merging a substantive change: clean working tree, bump version, annotated tag `v<version>`, publish to npm, and keep the GitHub milestone/release updated.

### npm release verification

Maintainers publish the verified candidate manually using the handoff rules in
`docs/00-project-rules.md` §4.1. Tag pushes run `.github/workflows/release.yml`,
which only verifies that the tagged version and the `latest` dist-tag already
exist on the public npm registry before creating or updating the GitHub Release;
the workflow never publishes a second copy. A missing version or mismatched
`latest` tag fails the workflow and prevents the GitHub Release.

## New DSH release smoke checklist

The plugin verifies an exact DeepSeek Harness BOM (`compatibility/dsh-bom.json`). A new DSH rc/stable is **unverified by default**, and lifecycle drift has broken activation before (#17), so run this checklist before moving the pin — in order, stopping at the first failure:

1. **Sandbox or isolated cloud first.** Prefer `pnpm run check` on Node matching `.nvmrc` in Cursor Cloud / an isolated `DSH_HOME`. Docker remains optional: edit the hardcoded `@deepseek-ai/dsh@0.1.1-rc.2` pin in `Dockerfile` (`dsh-installed` / `rc2-compatibility` stages — there is no build-arg) to the candidate DSH version, then build those targets green. This catches activation/lifecycle breakage without touching a real profile.
2. **Real profile install (maintainer machine).** Install the candidate tarball into an existing `dsh web` profile and restart that process once; the plugin must activate with only `webServer` required and no Cordis injection failures in the logs.
3. **Settings surface.** Settings → Coding OAuth renders all four tabs (Accounts / Gateway / Capabilities / About).
4. **Credentials survive.** Every previously signed-in provider card (Grok / Codex / Kimi / Claude) still shows signed-in — an upgrade must never migrate or reset OAuth credential files.
5. **Live routes.** `DSH_WEB_URL=http://127.0.0.1:3080 pnpm run verify:deployed` (status route + catalog + registered routes), then `smoke:deployed` with `DSH_RESTORE_PROVIDER` / `DSH_RESTORE_MODEL` set for one streamed reply per credentialed OAuth route.
7. **Then move the pin.** Update `compatibility/dsh-bom.json`, the `dsh.compatibility` block in `package.json`, and the README / INSTALL compatibility notes in the same commit. Recording a host under `candidates[]` (for example `0.1.5-rc.1`) is not a promotion.

## Reporting security issues

Do not open a public issue for a credential or account-safety problem. Follow the compliance/safety policy in `README.md`; for anything sensitive, contact a maintainer directly rather than pasting tokens or credentials anywhere.

## Document layers reminder

- **Publishable**: root `README.md` + the community-language READMEs, `INSTALL.md`, `CHANGELOG.md`, `LICENSE`, `NOTICE`, `docs/00-project-rules.md`, `docs/02-architecture.md` + `docs/02-architecture.zh-CN.md`, and other generic `docs/` files. These ship to npm and git — keep them privacy-free.
- **Local-only**: `docs/local/` and `reference/` are git-ignored and never shipped. Do not reference them from publishable docs.

If you are not sure whether a detail is publishable, keep it in the local-only layer or ask a maintainer.
