feat: Add blocking license standard asserting the copyright holder

Standard

license (new). Also touches presentation, which hands LICENSE over.

Change type

  • Add new standard
  • Modify existing standard's check.sh (add, remove, or change an assertion)
  • Modify existing standard's templates

What does this MR do and why?

standards/license asserts two things: LICENSE exists at the repository root, and its copyright line names GitLab Inc. It ships blocking, at introduced.

LICENSE is the one file in a repository that is a legal instrument, and until now nothing in the estate read the holder named on it. presentation asserted check_file_exists LICENSE, which a LICENSE naming anyone at all satisfies, and it asserted it at the advisory tier because presentation's subject is house style.

The existence gate and templates/LICENSE move out of presentation in the same MR. Asserting one file in two standards at two tiers gives it two owners and two verdicts.

Failure mode this addresses

A LICENSE naming a party that does not hold the copyright, on a repository under gitlab-com/.

The copyright sweep of 2026-08-11 read every license-shaped file at its default branch across 144 projects in three namespaces and found 47 copyright lines naming someone other than GitLab Inc.: 19 name a private individual, 15 name GitLab Public Sector, 6 name GitLab demonstration, and the rest name GitLab B.V. (the Netherlands employing entity, not the code copyright holder since the 2015 rename) or a bare GitLab.

Two of those classes are not stylistic. A LICENSE naming a private individual on a gitlab-com/ repository asserts personal ownership of GitLab work product. GitLab Public Sector and GitLab demonstration are wordmarks, not incorporated bodies, so a grant from either comes from a party that cannot hold copyright. Fifteen repositories converged on the same non-entity independently, without copying from one another, which is what an unenforced string in a prose table produces. The correct string was already written down in standards/presentation/templates/wordmark.md, enforced by nothing.

This assertion is the estate's own worked example. .gitlab/merge_request_templates/Standard-Change.md has offered, since it was written, as the canonical illustration of an assertion delta:

- REMOVE: file-contains:LICENSE:^Copyright \(c\) Andrew Dunn$

Someone imagined this exact assertion, wrote it into the template every future standard change would be modelled on, and never shipped it. The 47 lines accumulated in the interval. This MR ships it.

Assertion deltas

- ADD:    file-exists:LICENSE                     (standards/license, via require_file)
- ADD:    file-contains:LICENSE:^Copyright( \(c\)| \(C\)| ©)?( [0-9]{4}(-([0-9]{4}|[Pp]resent))?)? GitLab Inc\.$
- REMOVE: file-exists:LICENSE                     (standards/presentation)

The estate gains one assertion and moves another. presentation goes from 14 to 13.

The regex leaves the copyright marker, the year, and the range form optional on purpose. Copyright (c) 2026 GitLab Inc., Copyright (c) 2011-present GitLab Inc. and the year-less Copyright GitLab Inc. all appear on correct gitlab-org LICENSE files, and none of those differences changes who owns the code. Pinning the year format would specify prose wording rather than a property, which the security-md relaxation earlier in this CHANGELOG established as the punitive shape. What the regex pins is the entity, anchored at both ends so a line naming a different holder cannot satisfy it by containing the right string somewhere else.

Control backing, and why this blocks

Tier follows control backing. compliance/unified.yaml gains one row:

field value
id outbound-license
standard license
family Supply chain
frameworks nist_800_53_r5: CM-10, ssdf_v1_1: PS.3.2
sectors dod, hipaa, finance, education
enforced_by project: reference/standards/license/check.sh
evidence path: LICENSE
status measured

measured is claimed under the schema's own definition: the enforcer cited under enforced_by exists today and produces the evidence cited under evidence. No framework was added to compliance/registry.yaml; both citations resolve against frameworks already declared there.

Status is introduced, because a new standard always ships at introduced first. Tier and status are separate axes. This is the provenance shape: introduced and blocking.

Scope, and what is left to the platform

The standard asserts existence and holder, and nothing below the copyright line. GitLab already detects the license type natively, reads the body against a license database, and reports the result on the project overview and through projects/:id?license=true as license.key and license.nickname. Re-deriving that in a regex would be a second, worse implementation of a platform feature. The division is stated in the SKILL.md scope section and named in front matter unasserted:, so it renders on the standard's card.

Also out of scope: whether the body is unmodified license text (a question for counsel), whether MIT was the right choice (a #legal consult, which is a process step and not a repository property), and prose licensing in a second file such as this repository's LICENSE-docs.

Consumer impact

Measured today against this exact regex, across the 58 non-archived projects in gitlab-com/public-sector:

verdict count
pass 40
fail on the holder assertion 11
fail on the presence gate 7

The 11 holder failures: 7 × Copyright (c) 2026 GitLab Public Sector (all under lab/project-trustchain/ and lab/project-lattice/), 2 × GitLab B.V. (stig-analyzer, lab/version-gitlab-com-simulation), 1 × Copyright (c) 2026 Andrew G. Dunn / GitLab (lab/project-lattice/paper), 1 × kaniko (Apache-2.0, no discrete Copyright line).

The 7 presence failures ship no LICENSE at all: bulkhead, manifold-demo, lab/collaboration, lab/sh-clickhouse-iac, lab/implementation-tracker, lab/duo-null, lab/compare-p1-upstream.

Three are expected to register a .reference.yaml deviation instead of being rewritten, because a fork keeps its upstream's LICENSE byte-for-byte: kaniko (Apache-2.0 carries its notice in NOTICE), stig-analyzer, and lab/version-gitlab-com-simulation. A fourth fork, lab/project-trustchain/gitlab-patches, tracks upstream at Copyright (c) 2011-present GitLab Inc. and passes unchanged. Three registered deviations is the price of catching the rest. If that list grows past a handful, the assertion is wrong and belongs back in an MR here.

Remediating the failing projects is out of scope for this MR and does not gate it. The standard is at introduced, adoption_phase governs how strictly a consumer treats a FAIL, and landing the assertion first is what stops the sweep having to be run by hand again.

Cutover schedule

None proposed here. introduced -> enforcing is a separate, later decision with its own MR, target tag, and date, per AGENTS.md.

Test plan

  • just guard passes: guard: 11 primitives, checked 20 file(s), 0 violation(s)
  • PIPELINE_REPO=... just validate passes, validate-binding at 32 controls / 20 standards, binding intact
  • just check passes against this repo: TOTAL: 20 standard(s) passed, 0 failed. This repository's own LICENSE already read Copyright (c) 2026 GitLab Inc. and needed no change.
  • just ship green end to end (guard, validate, check-self, build, agent)
  • just sync run; the ## Verify block is generated, not hand-written
  • Verified able to fail, not only able to pass. Ran check.sh against six real consumer trees fetched from their default branches: lab/project-lattice/paper, stig-analyzer and kaniko fail the holder assertion, bulkhead fails the presence gate, lab/project-trustchain/meta and tach pass.
  • Template moved, not copied: standards/presentation/templates/LICENSE is now standards/license/templates/LICENSE, and the one link to it is updated.

Reviewer checklist

  • SKILL.md front matter is valid YAML (asserted by validate-registry)
  • check.sh sources ../../_lib.sh and calls report
  • Only closed-set primitives used (require_file, check_file_contains). No primitive extension.
  • templates/ present, ## Template heading present
  • Standard appears in the README contracts table and in standards/registry.yaml (Mandatory group)
  • Standard renders in the built site and the agent surface (site/dist/standards/license/{index.md,check.sh,templates})

References

Motivating research: the copyright mis-attribution sweep of 2026-08-11, 144 projects across three namespaces, remote-verified at each project's default branch. Operator approved this addition on 2026-08-11.

Edited by Andrew Dunn

Merge request reports

Loading