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
136 changes: 136 additions & 0 deletions docs/CSL_ONBOARDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# CSL 1.0 onboarding with darnit

How to make any specification repository compliant with the
[Community Specification License 1.0](https://github.com/CommunitySpecification/1.0)
using the `community-spec` framework (`darnit-csl`), and reproduce the process
we used for DSSE, in-toto, TUF, and Uptane.

## What it produces

The required CSL file set, generated from templates and audited:

| File | Content |
|---|---|
| `governance/00-contributor-license-agreement.md` | CSL Contributor License Agreement 1.0 (verbatim, with working links to the upstream documents) |
| `governance/01-community-specification-license-v1.md` | Community Specification License 1.0 (verbatim) |
| `governance/02-scope.md` | The Working Group's Scope — **you draft this; it bounds the patent commitment** |
| `governance/03-notices.md` | Notices with the Code of Conduct pointer (org-first, see below) |
| `governance/04-license.md` | Dual license: CSL for the spec, your chosen license for source/sample code |
| `governance/05-governance.md` | Full CSL Governance Policy, or a stub referencing umbrella governance (e.g. CNCF) |
| `README` links | A "Governance & Licensing" section pointing at the files above |

Seven controls verify it: `CSL-01.01` (CLA), `CSL-01.02` (license, detected by
content under any filename), `CSL-02.01` (scope is real, not the bracketed
placeholder), `CSL-03.01` (notices name a real contact **or** point at an
org-level Code of Conduct — blanks, placeholder names, and leftover drafting
guidance all FAIL), `CSL-04.01` (dual license), `CSL-05.01` (CSL-specific
governance), `CSL-06.01` (README discoverability).

## What you must supply

1. **Scope prose.** Write what the Working Group standardizes and what it
excludes, grounded in the repository's actual documents — never generic
filler, never the bracketed template text. This is a legal boundary, so a
human must review it before any PR.
2. **Code of Conduct — org-first.** If the project already has a Code of
Conduct (its own `CODE_OF_CONDUCT` file, the org's community or `.github`
repo, or a foundation CoC such as CNCF, LF, or JDF), point at it:
`coc_policy='org'` plus `coc_reference` — one markdown sentence linking
that document. The notices then name no individuals; the linked CoC
defines the reporting procedure. The remediate tool refuses individual
contacts when the repo itself already ships a CoC file. Only when no such
CoC exists, supply two named individuals with an email or handle each —
never a mailing list.
3. **Source-code license** for sample/reference code (e.g. `Apache-2.0`, `MIT`).
4. **Governance mode**: `csl` (ship the full CSL policy) or `umbrella`
(reference existing governance, e.g. a CNCF project charter).

## Path 1 — one-command driver (recommended)

From the darnit repo root:

```bash
# 1. Baseline audit; also writes a scope template for you to fill in
uv run python scripts/csl_onboard.py --repo <owner>/<repo> --audit-only

# 2a. Project already has a Code of Conduct (org-first):
uv run python scripts/csl_onboard.py --repo <owner>/<repo> \
--scope-file scopes/<repo>.md \
--coc-policy org \
--coc-reference "X is a [CNCF](https://www.cncf.io/) project and follows the [X Code of Conduct](<url>)." \
--code-license Apache-2.0 --spec-name "<Spec Name>"

# 2b. No existing CoC anywhere — named-individuals fallback:
uv run python scripts/csl_onboard.py --repo <owner>/<repo> \
--scope-file scopes/<repo>.md \
--contacts "Name One <a@x.org>, Name Two <b@y.org>" \
--code-license Apache-2.0 --spec-name "<Spec Name>"
```

The driver clones, branches, audits, remediates from your scope file, scans
every generated file for placeholder text, re-audits, and prints the fork/PR
commands. Nothing is written without `--scope-file`.

## Path 2 — agentic, via MCP (`darnit serve`)

Register the server with your MCP client (Claude Code, etc.):

```json
{ "mcpServers": { "darnit-csl": {
"command": "uv",
"args": ["run", "darnit", "serve", "--framework", "community-spec"] } } }
```

The client gets three tools: `audit_community_spec`,
`list_community_spec_controls`, and `remediate_community_spec`. The intended
loop: audit → the agent drafts the Scope **from the repository's own
specification documents** and **checks for an existing Code of Conduct**
(repo file, org community/.github repo, foundation CoC) → you review → the
agent calls `remediate_community_spec(local_path, scope=..., coc_policy=...,
coc_reference=... / coc_contacts=...)` → inline re-audit. The tool enforces
org-first itself: if the repo carries its own CoC file, individual contacts
are rejected with a pointer to org mode. Under `serve`, content-quality
checks run through the LLM first; under plain `darnit audit` they fall back
to deterministic checks.

## Path 3 — local checkout, minimal

`scripts/csl_manual_remediate.py` runs remediation + re-audit against a repo
you have already cloned: edit its `ANSWERS` dict (scope, contacts or
`csl_coc_policy='org'` + `csl_coc_reference`, licenses, mode — this edit
stays local, don't commit it), then:

```bash
uv run python scripts/csl_manual_remediate.py <path-to-repo>
```

## Reviewing and opening the PR

- `git status` in the target repo must show **only** `governance/*` changes
(plus README links on first run). Never commit `.project/` — that is
darnit's per-repo state.
- Read `02-scope.md` and `03-notices.md` yourself; the audit checks form,
humans check meaning.
- Commit, push to your fork, open a **draft** PR, and note in the description
that the spec is CSL-licensed and code is under the chosen license.

## Re-running after review

`02-scope.md` and `03-notices.md` are regenerated on every run
(`overwrite = true`). Verbatim legal files (`00`, `01`, `04`, `05`) are
protected (`overwrite = false`): to regenerate one — e.g. after a template
fix — delete the file first, then re-run.

## Known upstream quirk

The upstream CSL repo renamed its files to the `NN-name.md` convention but
never updated its own CLA boilerplate, which still cites old `N._Name.md`
names. The `csl_cla` template links to the files that actually exist; if a
reviewer flags "broken filenames," point them upstream.

## Real examples of the org-first rule

- **in-toto** (CNCF): points at the [in-toto Community Code of Conduct](https://github.com/in-toto/community/blob/main/CODE-OF-CONDUCT.md), which abides by the CNCF CoC.
- **TUF** (CNCF): points at the [TUF Community Code of Conduct](https://github.com/theupdateframework/community/blob/main/CODE-OF-CONDUCT.md).
- **Uptane** (LF/JDF): points at the [JDF Code of Conduct](https://jointdevelopment.org/policies/code-of-conduct/).
- **DSSE** (no org-level CoC): named individuals — the fallback case.
68 changes: 68 additions & 0 deletions packages/darnit-csl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# darnit-csl

Community Specification License (CSL 1.0) compliance for
[darnit](https://github.com/kusari-oss/darnit).

It audits — and remediates — the file set the Linux Foundation / Joint
Development Foundation asks for in a repository that develops a specification
under the **Community Specification License 1.0**:

| File (in `governance/`, `license/`, or repo root) | Control | What it is |
|---|---|---|
| `00-contributor-license-agreement.md` | `CSL-01.01` | Contributor License Agreement (static) |
| `01-community-specification-license-v1.md` | `CSL-01.02` | CSL 1.0 license text (static) |
| `02-scope.md` | `CSL-02.01` | Working Group **Scope** — per-project; can be LLM-drafted |
| `03-notices.md` | `CSL-03.01` | **Notices** + Code of Conduct contact(s) |
| `04-license.md` | `CSL-04.01` | Dual license: CSL for the spec, MIT/Apache for code |
| `05-governance.md` | `CSL-05.01` | Governance — full CSL policy **or** a reference to an umbrella project's governance |
| README links | `CSL-06.01` | Scope and Notices are discoverable from the README |

## How each control works (the 7-step pattern)

1. Look in `.project/` for confirmed context.
2. Does the file exist (`governance/`, `license/`, or repo root)?
3. Is the content valid — i.e. no leftover template placeholder? If yes, **PASS**, done.
4. If not, collect data and ask the user (`requires_context` prompts).
5. Persist the answers to `.project/`.
6. Generate the file from a template (`file_create`).
7. Refine the generated file from project context via the LLM (`llm_enhance`).

The two content-validated files (`02-scope.md`, `03-notices.md`) only pass when
the template placeholder is gone — a deterministic check that runs in
`darnit audit` without an LLM, with an `llm_eval` nuance pass for the MCP server.

## Governance & Code of Conduct: reference vs. subsume

Projects that already follow an umbrella governance / CoC (e.g. CNCF) do **not**
need to adopt the CSL versions wholesale:

- `csl_governance_mode = "umbrella"` generates a short `05-governance.md` that
**references** the existing governance (it is not superseded) and documents the
Community Specification roles and decision-making on top of it.
- `csl_coc_policy = "umbrella"` makes `03-notices.md` cite an existing Code of
Conduct (e.g. the CNCF CoC) instead of shipping the CSL one.

The defaults adopt the full CSL governance and ship CSL contacts; the umbrella
modes are opt-in.

## Try it

```bash
# Audit a spec repo against CSL 1.0
uv run darnit audit --framework packages/darnit-csl/src/darnit_csl/community-spec.toml /path/to/spec-repo

# Audit just the required-files profile
uv run darnit audit --framework packages/darnit-csl/src/darnit_csl/community-spec.toml --profile required_files /path/to/spec-repo

# Show the execution plan
uv run darnit plan --framework packages/darnit-csl/src/darnit_csl/community-spec.toml /path/to/spec-repo
```

Once installed (`pip install darnit-csl`), it is discovered by name:
`darnit audit --framework community-spec /path/to/spec-repo`.

## License

Apache-2.0. The bundled CSL templates are reproduced from the
[Community Specification 1.0](https://github.com/CommunitySpecification/1.0)
repository.
48 changes: 48 additions & 0 deletions packages/darnit-csl/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[project]
name = "darnit-csl"
version = "0.1.0"
description = "Community Specification License (CSL 1.0) compliance implementation for darnit"
readme = "README.md"
requires-python = ">=3.11"
license = "Apache-2.0"
authors = [
{ name = "Kusari", email = "info@kusari.dev" },
]
keywords = ["compliance", "darnit", "community-specification", "csl", "plugin"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Documentation",
]
dependencies = [
"darnit-core>=0.1.0",
]

[project.urls]
Homepage = "https://github.com/kusari-oss/darnit"
Repository = "https://github.com/kusari-oss/darnit"
Issues = "https://github.com/kusari-oss/darnit/issues"

# Discoverability: the implementation entry point.
[project.entry-points."darnit.implementations"]
community-spec = "darnit_csl:register"

# Framework TOML discovery (locate the config without importing the package).
[project.entry-points."darnit.frameworks"]
community-spec = "darnit_csl:get_framework_path"
community-spec-optional = "darnit_csl:get_optional_framework_path"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/darnit_csl"]

# community-spec.toml and templates/ live inside src/darnit_csl/, so hatchling
# includes them in the wheel automatically.
68 changes: 68 additions & 0 deletions packages/darnit-csl/src/darnit_csl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""darnit-csl — Community Specification License (CSL 1.0) compliance plugin.

Adds a darnit compliance standard for repositories that develop specifications
under the Community Specification License 1.0. It audits for, and remediates,
the file set the Linux Foundation / Joint Development Foundation requires
(CLA, license, scope, notices, dual-license, governance).
"""

from pathlib import Path

from .implementation import CommunitySpecImplementation


def register() -> CommunitySpecImplementation:
"""Entry point called by darnit plugin discovery.

Wired in pyproject.toml via:

[project.entry-points."darnit.implementations"]
community-spec = "darnit_csl:register"
"""
return CommunitySpecImplementation()


def get_framework_path() -> Path:
"""Entry point for framework TOML config discovery.

Wired in pyproject.toml via:

[project.entry-points."darnit.frameworks"]
community-spec = "darnit_csl:get_framework_path"
"""
return CommunitySpecImplementation().get_framework_config_path()


def get_optional_framework_path() -> Path:
"""Entry point for the OPTIONAL (facultative) framework TOML.

Registers the separate `community-spec-optional` framework, which presence-
checks the optional Community Specification files (06 contributing, 07 spec-
template, 08 code-of-conduct). Kept separate so it never affects the required
CSL compliance score. Wired in pyproject.toml via:

[project.entry-points."darnit.frameworks"]
community-spec-optional = "darnit_csl:get_optional_framework_path"
"""
from importlib.resources import files

resource = files(__package__) / "community-spec-optional.toml"
path = Path(str(resource))
if not path.is_file():
raise FileNotFoundError(
f"community-spec-optional.toml not found in installed darnit_csl "
f"package at {path}. This indicates a broken build; check the "
f"wheel's force-include configuration."
)
return path


__all__ = [
"CommunitySpecImplementation",
"register",
"get_framework_path",
"get_optional_framework_path",
]

# Register custom sieve handlers on import.
from . import handlers # noqa: E402,F401
Loading
Loading