Skip to content
Open
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
102 changes: 102 additions & 0 deletions dev-team/commands/dev-license.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: ring:dev-license
description: Apply or switch the license for the current repository
argument-hint: "[apache|elv2|proprietary] [options]"
---

Apply or switch the license for the current repository.

## Usage

```
/ring:dev-license [license-type] [options]
```

## Arguments

| Argument | Required | Description |
|----------|----------|-------------|
| `license-type` | No* | One of: `apache`, `elv2`, `proprietary` |

*If omitted, the skill will detect the current license and ask which to apply.

## Options

| Option | Description | Example |
|--------|-------------|---------|
| `--dry-run` | Show what would change without modifying files | `--dry-run` |
| `--year YEAR` | Override copyright year (default: current year) | `--year 2025` |
| `--holder NAME` | Override copyright holder (default: Lerian Studio Ltd.) | `--holder "Lerian Studio Ltd."` |

## License Types

| Type | Full Name | SPDX | Use Case |
|------|-----------|------|----------|
| `apache` | Apache License 2.0 | `Apache-2.0` | Open source (e.g., Midaz core) |
| `elv2` | Elastic License v2 | `Elastic-2.0` | Source-available Lerian products |
| `proprietary` | Lerian Studio General License | `LicenseRef-Lerian-Proprietary` | Internal/closed repos |

## Examples

```bash
# Apply Apache 2.0 license
/ring:dev-license apache

# Switch to ELv2
/ring:dev-license elv2

# Apply proprietary license with specific year
/ring:dev-license proprietary --year 2024

# Check what would change without modifying
/ring:dev-license apache --dry-run

# Detect current license (interactive)
/ring:dev-license
```

## What It Does

1. **Detects** current license (LICENSE file, source headers, SPDX identifiers)
2. **Confirms** change with user (if switching from an existing license)
3. **Writes** the LICENSE file with the full license text
4. **Updates** all source file headers (.go, .ts, .js) to match
5. **Updates** SPDX identifiers in go.mod/package.json (if present)
6. **Updates** README.md license badge/section (if present)
7. **Validates** all files have consistent headers

## Related Commands

| Command | Description |
|---------|-------------|
| `/ring:dev-cycle` | Development cycle (includes license check at Gate 0) |
| `/ring:dev-refactor` | Codebase analysis (may detect license inconsistencies) |

---

## MANDATORY: Load Full Skill

**This command MUST load the skill for complete workflow execution.**

```
Use Skill tool: ring:dev-licensing
```

The skill contains the complete 4-gate workflow with:
- License detection and identification
- User confirmation gate
- Agent dispatch for header updates
- Validation with consistency checks
- Anti-rationalization tables
- Pressure resistance scenarios

## Execution Context

Pass the following context to the skill:

| Parameter | Value |
|-----------|-------|
| `license_type` | First argument: `apache`, `elv2`, or `proprietary` (if provided) |
| `dry_run` | `true` if `--dry-run` flag present |
| `copyright_year` | Value of `--year` option (default: current year) |
| `copyright_holder` | Value of `--holder` option (default: `Lerian Studio Ltd.`) |
83 changes: 64 additions & 19 deletions dev-team/docs/standards/golang/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -844,23 +844,64 @@ grep -rn "CREATE TABLE\|ALTER TABLE\|DROP TABLE" --include="*.go" ./internal
| Compliance audit failures | Audit-ready codebase |
| Inconsistent attribution | Uniform legal protection |

### Required Format (Elastic License 2.0)
### Important: License Is Per-Repository

Lerian uses three license types, chosen per-app. The actual header text MUST match the LICENSE file in the repository root. Use the `/ring:dev-license` command (or the `ring:dev-licensing` skill) to apply or switch licenses consistently across a repository.

| License | SPDX Identifier | When Used |
| ------- | --------------- | --------- |
| Apache 2.0 | `Apache-2.0` | Open source projects (e.g., Midaz core) |
| Elastic License v2 | `Elastic-2.0` | Source-available Lerian products |
| Proprietary | `LicenseRef-Lerian-Proprietary` | Internal/closed repositories |

### Required Format: Apache 2.0

```go
// Copyright (c) 2025 Lerian Studio Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package yourpackage
```

### Required Format: Elastic License 2.0

```go
// Copyright (c) 2025 Lerian Studio Ltd.
// Use of this source code is governed by the Elastic License 2.0
// that can be found in the LICENSE file.

package yourpackage
```

### Required Format: Proprietary (Lerian Studio General License)

```go
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License 2.0;
// you may not use this file except in compliance with the Elastic License 2.0.
// Copyright (c) 2025 Lerian Studio Ltd. All rights reserved.
// This source code is proprietary and confidential.
// Unauthorized copying of this file is strictly prohibited.

package yourpackage
```

### Header Components

| Component | Value | Notes |
| ----------------- | --------------------- | ----------------------------------------- |
| Copyright holder | `Elasticsearch B.V.` | Fixed for all projects |
| License reference | `Elastic License 2.0` | Or as specified in LICENSE file |
| LICENSE location | Inline in header | No separate LICENSE file reference needed |
| Component | Value | Notes |
| ----------------- | ------------------------------ | -------------------------------------------------- |
| Copyright holder | `Lerian Studio Ltd.` | Default for all Lerian projects |
| Copyright year | Current year (e.g., `2025`) | Update when making significant changes |
| License reference | Depends on repository LICENSE | MUST match the LICENSE file in the repo root |
| LICENSE location | Inline in header | No separate LICENSE file reference needed |

Comment on lines +903 to 905
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Header component guidance conflicts with ELv2 required template

Line 904-Line 905 says no LICENSE-file reference is needed, but the ELv2 required header template (Line 879-Line 883) explicitly requires LICENSE reference. This contradiction can produce non-compliant headers.

🔧 Proposed fix
-| LICENSE location  | Inline in header               | No separate LICENSE file reference needed           |
+| LICENSE location  | Repository root (`LICENSE`)    | Header text MUST align with repository LICENSE; ELv2 header explicitly references LICENSE |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| License reference | Depends on repository LICENSE | MUST match the LICENSE file in the repo root |
| LICENSE location | Inline in header | No separate LICENSE file reference needed |
| License reference | Depends on repository LICENSE | MUST match the LICENSE file in the repo root |
| LICENSE location | Repository root (`LICENSE`) | Header text MUST align with repository LICENSE; ELv2 header explicitly references LICENSE |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dev-team/docs/standards/golang/core.md` around lines 903 - 905, The table
entry "LICENSE location | Inline in header" conflicts with the "ELv2 required
header template" described earlier; update the header guidance so the header
component includes an explicit LICENSE file reference to match ELv2
requirements. Concretely, modify the table row for "LICENSE location" and the
"Header component" guidance to state that the header must include a "LICENSE:
<repository LICENSE>" reference (or point to the LICENSE file in the repo root)
and ensure the "License reference" row remains "MUST match the LICENSE file in
the repo root"; update the documentation text near the "ELv2 required header
template" and the table entries ("License reference" and "LICENSE location") so
they are consistent.

### Files That MUST Have Headers

Expand All @@ -883,9 +924,9 @@ package yourpackage
### Correct Examples

```go
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License 2.0;
// you may not use this file except in compliance with the Elastic License 2.0.
// Copyright (c) 2025 Lerian Studio Ltd.
// Use of this source code is governed by the Elastic License 2.0
// that can be found in the LICENSE file.

package bootstrap

Expand All @@ -896,9 +937,9 @@ import (
```

```go
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License 2.0;
// you may not use this file except in compliance with the Elastic License 2.0.
// Copyright (c) 2025 Lerian Studio Ltd.
// Use of this source code is governed by the Elastic License 2.0
// that can be found in the LICENSE file.

package bootstrap_test

Expand All @@ -916,18 +957,22 @@ package model
import "time"

// ❌ FORBIDDEN: Wrong format (missing full license text)
// Copyright Elasticsearch B.V.
// Copyright Lerian Studio
// Licensed under Elastic License 2.0
package model

// ❌ FORBIDDEN: Header after package declaration
package model

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License 2.0;
// you may not use this file except in compliance with the Elastic License 2.0.
// Copyright (c) 2025 Lerian Studio Ltd.
// Use of this source code is governed by the Elastic License 2.0
// that can be found in the LICENSE file.

import "time"

// ❌ FORBIDDEN: Header from a different license than the repo LICENSE file
// (e.g., Apache header in an ELv2 repo, or ELv2 header in an Apache repo)
// Headers MUST match the LICENSE file in the repository root
```

### Verification Commands
Expand Down
22 changes: 22 additions & 0 deletions dev-team/skills/dev-cycle/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,28 @@ PM team task files often omit external_dependencies. If the codebase uses postgr
Multi-tenant state is detected here and passed to Gate 0 (implementation) and Gate 0.5G (verification). See [multi-tenant.md](../../docs/standards/golang/multi-tenant.md) for the canonical model and compliance criteria.
</auto_detect_reason>

### License Detection (Advisory)

Detect the repository license at cycle start. This check is advisory — it does not block Gate 0. If no license is found, prompt the user; if the user declines, log a warning and proceed.

```text
7. Detect repository license:
license_type = "unknown"

- ls LICENSE LICENSE.md LICENSE.txt 2>/dev/null
- If found:
- grep -l "Apache License" LICENSE* → license_type = "apache"
- grep -l "Elastic License" LICENSE* → license_type = "elv2"
- grep -l "All rights reserved.*Lerian" LICENSE* → license_type = "proprietary"
- If not found (no LICENSE file):
→ Ask user: "No LICENSE file detected. Which license should this repository use? [apache|elv2|proprietary|skip]"
→ If user selects a license: invoke Skill("ring:dev-licensing") with chosen type
→ If user selects "skip": log "⚠️ WARNING: No LICENSE file. License headers may be inconsistent."

Store: state.license_type = license_type
Log: "License detected: {license_type}"
Comment on lines +1851 to +1856
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Persist the selected license value before writing state

After the prompt flow (Line 1851-Line 1853), state.license_type is still assigned from license_type, which starts as "unknown". If the user selects a license, that selected value must be assigned before Line 1855.

🔧 Proposed fix
-7. Detect repository license:
-   license_type = "unknown"
+7. Detect repository license:
+   license_type = "unknown"
+   selected_license = ""
@@
-   - If not found (no LICENSE file):
+   - If not found (no LICENSE file):
      → Ask user: "No LICENSE file detected. Which license should this repository use? [apache|elv2|proprietary|skip]"
-     → If user selects a license: invoke Skill("ring:dev-licensing") with chosen type
+     → If user selects a license:
+       - selected_license = {user_selection}
+       - license_type = selected_license
+       - invoke Skill("ring:dev-licensing") with chosen type
      → If user selects "skip": log "⚠️ WARNING: No LICENSE file. License headers may be inconsistent."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dev-team/skills/dev-cycle/SKILL.md` around lines 1851 - 1856, The code
assigns state.license_type from license_type which remains "unknown" because the
selected value isn't persisted after the prompt flow; update the flow so that
when the user selects a license (the branch that invokes
Skill("ring:dev-licensing") with the chosen type) you set state.license_type =
<chosen value> immediately before logging "License detected: {license_type}"
(and ensure the "skip" branch still sets state.license_type appropriately if
desired), so that state.license_type reflects the user selection rather than the
initial "unknown".

```

---

## Step 2: Gate 0 - Implementation (Per Execution Unit)
Expand Down
Loading