Skip to content

Latest commit

 

History

History
315 lines (256 loc) · 15.6 KB

File metadata and controls

315 lines (256 loc) · 15.6 KB

Contributing to PDPP

Thanks for your interest in PDPP. This project is a protocol plus a forkable reference implementation, so contributions range from protocol-text edits to docs and the public site.

Two kinds of contribution now start in a different repository:

  • Connector source begins in PDP-Connect/data-connectors. This repository keeps a retained frozen copy under packages/polyfill-connectors/connectors/. A pull request that changes those files fails the write-freeze guard in .github/workflows/polyfill-connectors.yml, unless it carries the connector-write-freeze-override label. Fixtures, docs, and scripts are not frozen. For the runnable mechanics — how to run a connector, validate manifests, and what the registry currently contains — see packages/polyfill-connectors/CONNECTORS.md.
  • Reference-implementation code lives under reference-implementation/; console code lives under apps/console/, both in PDP-Connect/data-connect.

This guide applies to human and AI contributors alike. It distills the durable conventions for working in this repo: the spec-first workflow for protocol and contract changes, test expectations, and pull-request conventions.

Ground rules

  • No personal data in the repo. Do not commit private names, personal handles, private absolute paths, or third-party personal references in docs, code, fixtures, or reports. Use role-neutral labels (the owner, an operator, your-pdpp-host.example.com) unless a real value is explicitly approved.

  • Connector fixtures are opt-in, and raw captures never leave your machine. Capturing live connector runs (PDPP_CAPTURE_FIXTURES=1) is something you choose to do locally; nothing captures or commits automatically. Raw captures under fixtures/<connector>/raw/ are gitignored and must never be committed. Turning a capture into a contributed fixture is a deliberate, separate act: run the scrubber (bin/scrub-fixtures.ts), then review the scrubbed output yourself as a human before committing it — the deterministic scrubber is a conservative first pass, not a guarantee, and DOM/HTML captures in particular can carry unredacted names. See packages/polyfill-connectors/docs/connector-authoring-guide.md for the full scrub-and-review workflow.

  • Respect the authority order. This repo has a strict authority order:

    1. Root PDPP specs (spec-*.md) define normative protocol semantics.
    2. Code and tests define what the current reference implementation actually does.
    3. OpenSpec (openspec/) defines project-level architecture and change planning.

    Public web spec pages are downstream copies of the root specs; pnpm spec:check enforces parity. OpenSpec is project-scoped and does not replace or compete with the normative PDPP specs.

  • Mind the voice. Before writing or editing prose in any spec, design note, README, site copy, operator/dashboard string, or release note, read docs/reference/voice-and-framing.md. It keeps PDPP-as-protocol above OAuth/RAR, separates Core from Collection Profile from reference implementation from operator console, and lists phrasings to avoid.

  • Run the spec prose gate. Edits to a root spec-*.md are checked in two passes. pnpm spec:prose runs scripts/spec-prose-lint.mjs, which fails on six mechanical faults: prose hard-wrapped mid-sentence, a lowercase RFC 2119 key word governing a protocol role (RFC 8174 gives those no normative force), a capitalized key word inside a note (notes are informative, so the requirement would be unenforceable), a sentence over 40 words, filler and meta-commentary, and a paragraph duplicated within the file. pnpm spec:prose:fix rejoins hard-wrapped prose and changes nothing else; the other five faults need a decision about meaning, so the tool will not guess. The spec-prose-gate GitHub Actions job runs the same linter on the spec files a pull request touches, as a ratchet: hard-wrap always fails, because spec:prose:fix repairs it mechanically and changes no word, but the five judgment rules fail only on a finding your pull request introduced. The job lints the same files as of the merge-base to build a baseline, and prints a finding already in that baseline as a warning. So pnpm spec:prose run by hand reports more than the job fails on — the specs carry pre-existing findings of every judgment kind, and clearing one means rewriting a normative sentence, which is a spec editor's decision that needs its own review rather than a blocker on unrelated work. Fix what you introduce; leave the backlog to a dedicated editorial pass. The second pass is a judged read against docs/reference/spec-writing-rubric.md, ten criteria — each with a one-line test and the standard it comes from — covering what a regex cannot decide, such as whether an obligation names the role that bears it and whether a registry section documents its registration policy.

AI assistance

Building with AI is welcome. If AI helped meaningfully, add Assisted-by: AI to each assisted commit and as a standalone final line in the pull request description so reviewers can calibrate their scrutiny; a local hook can add the commit trailer for you (.github/hooks/prepare-commit-msg). It is vendor-neutral and sits alongside your DCO sign-off. We review contributions on whether they are good, not how they were made.

Spec-first workflow (OpenSpec)

This repo is spec-driven. When you are asked to design, plan, refactor, or introduce a non-trivial feature, write it as an OpenSpec change before writing code, and keep the two in lockstep afterward.

Read openspec/README.md before non-trivial planning work — it is the local rulebook for the OpenSpec lifecycle, closeout, and the design-note intake lane.

When OpenSpec applies

Write a change proposal when any of these are true:

  • You are introducing a new capability, new dependency, or new architectural boundary.
  • You are changing a durable contract (schemas, wire formats, endpoints, grant shapes, manifest fields).
  • You are modifying behavior a reviewer (standards body, a forker, a future you) should be able to audit after the fact.
  • The request asks you to "write it up," "propose," "plan," "design," or "spec it out."

In one line: OpenSpec is for design and contract decisions — the choices a future reviewer, forker, or standards body must be able to audit.

Do not open a change for work that carries no durable design decision:

  • Typos, comment edits, formatting, or a one-line bug fix.
  • A localized refactor or test tweak that preserves behavior and touches no contract.
  • Dependency bumps, lockfile churn, or CI/tooling config with no protocol impact.
  • Anything you could describe as "just fix it" without needing to explain a tradeoff.

For those, skip OpenSpec and just do the work — a stray proposal for a minor fix is noise that dilutes the changes that actually record a decision.

Shape of a change

Changes live under openspec/changes/<change-name>/:

  • proposal.md — short. ## Why, ## What Changes, ## Capabilities (Modified / Added / Removed), ## Impact. State facts, no novel prose.
  • design.md — rationale, alternatives considered, what is and isn't in scope, acceptance checks. This is where you show the thinking.
  • tasks.md — numbered sections with checkbox items, each small enough for one commit. Include an "Acceptance checks" section with reproducible steps.
  • specs/<capability>/spec.md — the capability-spec delta, using ## ADDED Requirements, ## MODIFIED Requirements, ## REMOVED Requirements. Every Requirement needs at least one #### Scenario: with **WHEN** / **THEN** phrasing. Requirements are normative (SHALL, SHALL NOT); scenarios are evidence. Do not put task lists in spec files.

Capability names mirror existing folders under openspec/specs/. Prefer updating an existing capability over minting a new one. If you are proposing multiple loosely related things, split them into separate changes.

Validating a change

Always run before handing back:

openspec validate <change-name> --strict

A valid change is the minimum bar; an invalid change is not ready for review.

Changing the protocol

If you change the protocol or a durable contract, update openspec/specs/<capability>/spec.md via a proper delta. Drive-by edits to capability spec files are not OK. When a change is fully implemented and accepted, its Requirement deltas fold into the durable specs/<cap>/spec.md and the change folder moves to openspec/changes/archive/ — don't archive work yourself unless asked.

Closeout, and where post-merge steps live

A change's tasks.md is for implementation work a contributor can finish and tick as it lands — code, tests, spec deltas. It is not an issue tracker. Post-merge steps that are not part of the change's implementation — a live production deploy, an acceptance pass, a migration to run against real data — do not belong as open checkboxes in tasks.md. Those boxes rarely get ticked (they depend on a deployment, not the merge), so they leave the change pseudo-active forever and the folder rots. Record such residual work in the issue tracker and, per openspec/README.md's closeout checklist, note it as a residual risk in the change — then let the change be archived. A ✓ Complete change should not stay active more than one merge cycle just because a live follow-up step is outstanding.

To catch changes that have effectively landed but are still sitting active, run:

pnpm openspec:archive-check

It lists changes under openspec/changes/ that look archive-due — all implementation tasks done, or the code they reference already exists on main. It's a non-blocking reminder (also wired as a pre-push warning and a report-only CI job), not a gate: folding a change's spec deltas into openspec/specs/ and archiving it is a maintainer step, so the check never fails a build or a push.

Building and testing

Install dependencies with pnpm install from the repo root (this is a pnpm workspace).

Common commands:

pnpm dev                              # reference AS/RS + operator console
pnpm reference-implementation:server  # reference server only
pnpm reference-implementation:cli --help
pnpm reference-implementation:test    # reference implementation tests
pnpm spec:check                       # root-spec / web-spec parity

Test scratch containment

Use pnpm test:scratch -- <command> [args...] for supported ad hoc test or host-writing verification commands. Canonical repository test aliases and reviewed CI test commands enter this boundary automatically. It creates one private, command-scoped host scratch root, passes it to ordinary descendants through the normal temporary-directory environment, and removes the verified root after normal completion, failure, SIGINT, or SIGTERM.

The wrapper preserves the exact child exit code and signal semantics. If a successful child leaves a root that cannot be verified and removed, the wrapper fails with infrastructure code 74 rather than reporting success. SIGKILL, OOM, host failure, detached descendants, container/service state, explicit external storage roots, and hard-coded paths that are not part of a reviewed migration remain outside the immediate-cleanup guarantee; verified stale roots are recovered conservatively by a later owner.

A raw node --test, tsx file.test.ts, or direct shell command is an intentional bypass unless you invoke it through pnpm test:scratch -- .... Do not set a global login-shell TMPDIR or delete ambient /tmp/TMPDIR to compensate. Keep per-test t.after()/try/finally cleanup for prompt local release, and do not move reviewed container paths or the shared dynamic n.eko flock into invocation scratch.

See the self-host quickstart for the full local and Docker workflows, and the reference implementation README in PDP-Connect/data-connect for the reference-implementation workflows.

The testing policy defines oracle selection, fixture and live-service rules, profile-gated evidence, test changes, and the coverage/mutation/selection strategy. test-accounting.manifest.json, runners, and CI remain the executable suite authority.

Pull request conventions

  • Branch and PR. All changes to protocol text, the reference implementation, and the site go through public pull requests. Non-trivial protocol, reference-contract, or architecture changes are tracked with an OpenSpec change before implementation.
  • Conventional Commits. Commit messages follow Conventional Commits. fix: creates a patch release and feat: creates a minor release; commits that do not follow the format do not release. Breaking-change markers are reserved for the intentional 1.0 milestone. See docs/reference/package-release-policy.md.
  • Keep specs and code in lockstep. If your PR implements an OpenSpec change, the change artifacts and the code should land together and stay consistent.
  • CI and merge gate. Every pull request must pass the required CI checks. Before pushing, run the focused tests and signoff gates required for the changed behavior; run pnpm spec:check when root or public spec parity is in scope. See the testing policy and docs/reference/ci-mode.md.

Developer Certificate of Origin (DCO)

Contributions to this project require a Developer Certificate of Origin sign-off. The DCO is a lightweight statement that you wrote the contribution or otherwise have the right to submit it under the project's licenses. It is not a copyright assignment. The full text is at developercertificate.org.

To sign off, add a Signed-off-by trailer to each commit message with your real name and email:

Signed-off-by: Jane Doe <jane.doe@example.com>

Git adds this automatically when you commit with the -s flag:

git commit -s -m "feat: add the thing"

The name and email in the sign-off must match the commit author. By signing off, you certify the statements in the DCO for that contribution. Pull requests whose commits are not signed off will be asked to add the sign-off before merge (you can amend existing commits with git rebase --signoff <base>).

Code of Conduct

This project follows the Contributor Covenant. By participating, you agree to uphold it. Report unacceptable behavior through the private channel described in CODE_OF_CONDUCT.md.

Governance

Maintainers and their scopes are listed in MAINTAINERS.md. For root protocol specifications, active maintainers act as editors for the current draft. Maintainer changes are proposed through pull request. This project is developed at PDP-Connect, a lab at LF Decentralized Trust; see the "Governance & stewardship" section of the README for the stewardship model, and GOVERNANCE.md for the programme.

If you are unsure

Ask whether a piece of work is change-worthy before inventing scope. "I'd draft this as an OpenSpec change add-X — yes?" is a one-line question that saves a rewrite.