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
5 changes: 3 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# specgen Development Guidelines

Auto-generated from all feature plans. Last updated: 2026-04-05
Auto-generated from all feature plans. Last updated: 2026-05-23

## Active Technologies
- C# 14, .NET 10 + `System.CommandLine`, `YamlDotNet`, `Scriban`, `CsCheck`, `xUnit`, `NSubstitute`, `BenchmarkDotNet` (002-dynamic-target-layout-engine)
- Local filesystem (source steering docs, target default YAML definitions, user override YAML, generated steering files) (002-dynamic-target-layout-engine)
- Local filesystem (spec/steering docs, built-in target YAML defaults, user override YAML, generated output files) (002-dynamic-target-layout-engine)
- Local filesystem (`steergen.config.yaml`, local pack cache, fetched pack archives/expanded files) (003-upgrade-pack-refs)

- C# 14, .NET 10 + `System.CommandLine`, `Scriban`, `CsCheck`, `xUnit`, `NSubstitute`, `BenchmarkDotNet` (001-steering-doc-transform)

Expand All @@ -25,10 +26,10 @@ tests/
C# 14, .NET 10: Follow standard conventions

## Recent Changes
- 003-upgrade-pack-refs: Added C# 14, .NET 10 + `System.CommandLine`, `YamlDotNet`, `Scriban`, `CsCheck`, `xUnit`, `NSubstitute`, `BenchmarkDotNet`
- 002-dynamic-target-layout-engine: Added C# 14, .NET 10 + `System.CommandLine`, `YamlDotNet`, `Scriban`, `CsCheck`, `xUnit`, `NSubstitute`, `BenchmarkDotNet`
- 002-dynamic-target-layout-engine: Added C# 14, .NET 10 + `System.CommandLine`, `YamlDotNet`, `Scriban`, `CsCheck`, `xUnit`, `NSubstitute`, `BenchmarkDotNet`

- 001-steering-doc-transform: Added C# 14, .NET 10 + `System.CommandLine`, `Scriban`, `CsCheck`, `xUnit`, `NSubstitute`, `BenchmarkDotNet`

<!-- MANUAL ADDITIONS START -->
<!-- MANUAL ADDITIONS END -->
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ jobs:
--filter "RunTargetLayoutRoutingTests|RunCatchAllRoutingTests|RunCompatibilityBaselineTests" \
--logger "trx;LogFileName=perf-gate-results.trx"

- name: Run pack upgrade performance budget tests
run: |
dotnet test specgen.slnx \
--no-build -c Release \
--filter "PackUpgradePerformanceTests" \
--logger "trx;LogFileName=pack-upgrade-perf-results.trx"

- name: Upload performance gate results
if: always()
uses: actions/upload-artifact@v4
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,9 @@ FodyWeavers.xsd
*.msm
*.msp
steergen.sln

# Cross-platform editor/system noise
.DS_Store
Thumbs.db
*.swp
.idea/
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<LangVersion>14.0</LangVersion>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest</AnalysisLevel>
<DefaultItemExcludes>$(DefaultItemExcludes);docs/samples/**</DefaultItemExcludes>
</PropertyGroup>

<!-- Release builds enable full optimizer for SC-006 performance envelope compliance -->
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Steergen is a .NET CLI tool that maintains a single set of steering and constitu
- [Template Packs](#template-packs)
- [Rules Packs](#rules-packs)
- [Authoring a Rules Pack](docs/authoring-rules-packs.md)
- [Sample Packs](docs/getting-started.md#12-sample-packs)
- [Exit Codes](#exit-codes)
- [Contributing](#contributing)
- [Troubleshooting](#troubleshooting)
Expand Down Expand Up @@ -128,8 +129,10 @@ steergen target remove kiro
| `steergen purge [options]` | Remove generated files managed by steergen |
| `steergen update [--templates] [--rules] [--force]` | Re-download configured packs |
| `steergen template-pack add <source> [--ref <ref>] [--path <localPath>]` | Add a template pack |
| `steergen template-pack upgrade --selector <source\|entryKey> [--tag <tag>]` | Upgrade the configured template pack reference |
| `steergen template-pack remove` | Remove the configured template pack |
| `steergen rules-pack add <source> [--ref <ref>] [--path <subdir>] [--scope <scope>]` | Add a rules pack |
| `steergen rules-pack upgrade --selector <source\|path> [--tag <tag>]` | Upgrade one configured rules pack reference |
| `steergen rules-pack remove <name>` | Remove a rules pack by name |
| `steergen rules-pack list` | List configured rules packs with status |

Expand Down Expand Up @@ -244,6 +247,14 @@ steergen update --templates --force

If no template pack is configured, the command exits with code 0 and reports that no pack source is configured.

Upgrade a specific template pack reference and persist a deterministic `(tag, commitSha)` tuple:

```bash
steergen template-pack upgrade --selector "github:acme-corp/steergen-templates|templates/default" --tag v2.1.0
```

When `--tag` is omitted, the command runs in `latest-refresh` mode, snapshots cache, purges the targeted cache copy, and refetches.

### Inspecting the template chain

See which templates come from which source:
Expand Down Expand Up @@ -331,6 +342,17 @@ SHA-pinned packs are skipped unless `--force` is specified:
steergen update --rules --force
```

Upgrade exactly one configured rules pack reference by canonical selector:

```bash
steergen rules-pack upgrade --selector "github:acme-corp/team-rules|backend-team" --tag v1.1.0
```

Selector escaping rules:

- Use `\\|` for a literal `|` inside either selector component.
- Use `\\\\` for a literal backslash.

### Inspecting rules packs

```bash
Expand Down
10 changes: 10 additions & 0 deletions docs/authoring-rules-packs.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,16 @@ steergen rules-pack add github:your-org/my-rules-pack --ref v1.0.0 --scope globa
- **Commit SHAs** (40-character hex) — Strongest guarantee of immutability. Steergen skips re-download for SHA-pinned packs.
- **Branches** (e.g., `main`) — Works but Steergen emits a warning recommending pinning. Content can change without notice.

### Consumer upgrade workflow

Consumers can upgrade a specific configured rules pack reference using canonical selectors:

```bash
steergen rules-pack upgrade --selector "github:your-org/my-rules-pack|backend-team" --tag v1.1.0
```

If `--tag` is omitted, Steergen performs a full targeted cache refresh (`latest-refresh`) and still re-pins to a deterministic `(tag, commitSha)` tuple.

---

## 8. Versioning and compatibility
Expand Down
44 changes: 44 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ This guide walks you through everything from installation to team workflows. You
8. [Adding steergen validation to your CI/CD pipeline](#8-adding-steergen-validation-to-your-cicd-pipeline)
9. [Writing project steering documents](#9-writing-project-steering-documents)
10. [Tips for teams](#10-tips-for-teams)
11. [Upgrading external packs safely](#11-upgrading-external-packs-safely)
12. [Sample packs](#12-sample-packs)

---

Expand Down Expand Up @@ -670,3 +672,45 @@ Once steergen is set up, you can add a short note to your project README directi
> Project rules live in `steering/project/` in this repository. Global rules are maintained in our shared policy repository referenced by `globalRoot`. After editing project rules, run `steergen run` and commit generated output changes.

For multi-project organisations, you can call out both roots explicitly in contributor docs.

---

## 11. Upgrading external packs safely

Use selector-targeted upgrade commands to refresh exactly one configured external reference at a time.

Rules pack upgrade:

```bash
steergen rules-pack upgrade --selector "github:acme-corp/governance-packs|backend-team" --tag v1.1.0
```

Template pack upgrade:

```bash
steergen template-pack upgrade --selector "github:acme-corp/steergen-templates|templates/default" --tag v2.1.0
```

Selector escaping:

- Use `\\|` for a literal `|` in selector components.
- Use `\\\\` for a literal backslash.

When `--tag` is omitted, Steergen runs in `latest-refresh` mode. It snapshots targeted cache state before purge/refetch and restores the snapshot if fetch fails, keeping config pins unchanged.

---

## 12. Sample packs

The repository includes ready-to-use, fully valid sample packs under `docs/samples/`:

- `docs/samples/template-pack/` — a valid template pack that provides a `claude-skills` target and renders category-based Claude Code skills.
- `docs/samples/rules-pack/` — a valid rules pack with `pack.yaml` and steering markdown documents.

You can validate the samples directly with:

```bash
steergen validate --config docs/samples/sample-validation.config.yaml
```

This is useful when you want a known-good starting point for authoring your own packs.
19 changes: 19 additions & 0 deletions docs/samples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Pack Samples

This folder contains valid, minimal sample packs for local experimentation.

## Contents

- `template-pack/`: sample template pack that provides a `claude-skills` target and uses a local layout rooted at `${generationRoot}/.claude/skills`, rendering category-based Claude Code skills under `.claude/skills/<category>-guidance/SKILL.md`.
- `rules-pack/`: sample rules pack with `pack.yaml` and valid steering markdown documents.
- `sample-validation.config.yaml`: config file used to validate these samples with `steergen validate`.

## Validation

Run from repository root:

```bash
steergen validate --config docs/samples/sample-validation.config.yaml
```

Expected result: exit code `0` and no validation errors.
5 changes: 5 additions & 0 deletions docs/samples/rules-pack/pack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: sample-rules-pack
version: 1.0.0
minSteergenVersion: 0.1.0
scope: global
rulesRoot: rules
17 changes: 17 additions & 0 deletions docs/samples/rules-pack/rules/platform/quality.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
id: platform-quality-v1
version: "1.0.0"
title: Platform Quality Rules
scope: global
status: active
---

# Platform Quality Rules

:::rule id="QUAL-001" mandatory="true" category="quality" tags="quality,testing"
All behavior changes must include automated tests that cover expected and error paths.
:::

:::rule id="QUAL-002" category="quality" tags="quality,reviewability"
Prefer small, composable changes that are easy to review and revert.
:::
17 changes: 17 additions & 0 deletions docs/samples/rules-pack/rules/security/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
id: security-authentication-v1
version: "1.0.0"
title: Authentication Baseline
scope: global
status: active
---

# Authentication Baseline

:::rule id="SEC-001" mandatory="true" category="security" tags="security,authentication"
All service endpoints must enforce authenticated access unless explicitly documented as public.
:::

:::rule id="SEC-002" mandatory="true" category="security" tags="security,secrets"
Secrets must not be stored in source control and must be loaded from managed secret stores.
:::
5 changes: 5 additions & 0 deletions docs/samples/sample-validation.config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
projectRoot: docs/samples/rules-pack/rules
registeredTargets:
- claude-skills
templatePack:
localPath: template-pack
27 changes: 27 additions & 0 deletions docs/samples/template-pack/claude-skills/default-layout.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
version: "1.0"

roots:
targetRoot: "${generationRoot}/.claude/skills"

routes:
- id: category-skill
scope: both
explicit: true
anchor: core
order: 10
match:
category: "*"
destination:
directory: "${targetRoot}/${category}-guidance"
fileName: "SKILL"
extension: ".md"

fallback:
mode: other-at-core-anchor
fileBaseName: SKILL

purge:
roots:
- "${targetRoot}"
globs:
- "**/*.md"
23 changes: 23 additions & 0 deletions docs/samples/template-pack/claude-skills/document.scriban
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{ path_parts = file_path | string.split "/" }}
{{ skill_name = path_parts[path_parts.size - 2] }}
{{ skill_topic = skill_name | string.replace "-guidance" "" }}
---
name: {{ skill_name }}
description: Applies {{ skill_topic }} guidance rules for this repository. Use when working on {{ skill_topic }} concerns.
disable-model-invocation: false
---

# {{ skill_topic | string.capitalize }} Guidance

Use these rules when making changes that affect {{ skill_topic }} behavior.

## Rules

{{ for rule in rules }}
{{ rule_text = rule.primary_text }}
{{ if rule_text == "" }}{{ rule_text = rule.explanatory_text }}{{ end }}
{{ if rule_text == "" }}{{ rule_text = rule.id }}{{ end }}
### {{ rule.id }}
{{ if rule.mandatory }}Required{{ else }}Recommended{{ end }}: {{ rule_text }}

{{ end }}
7 changes: 7 additions & 0 deletions docs/samples/template-pack/pack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: sample-template-pack
version: 1.0.0
minSteergenVersion: 0.1.0
providedTargets:
- targetId: claude-skills
defaultLayout: claude-skills/default-layout.yaml
description: Generate Claude Code skill files grouped by rule category.
34 changes: 34 additions & 0 deletions specs/003-upgrade-pack-refs/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Specification Quality Checklist: Upgrade External Pack References

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-05-23
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- Validation pass completed in one iteration; no unresolved issues.
43 changes: 43 additions & 0 deletions specs/003-upgrade-pack-refs/contracts/cli-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# CLI Contract: Pack Upgrade Commands

## Commands

### Rules Pack Upgrade
```text
steergen rules-pack upgrade --selector <source|path-or-entry-key> [--tag <tag>]
```

### Template Pack Upgrade
```text
steergen template-pack upgrade --selector <source|path-or-entry-key> [--tag <tag>]
```

## Inputs
- `--selector` (required): canonical composite selector that uniquely identifies exactly one configured pack reference.
- `--tag` (optional): explicit tag to fetch. If omitted, command performs latest refresh by purging and refetching targeted cache.

Selector escaping rules:
- Use `\\|` for a literal `|` in either selector component.
- Use `\\\\` for a literal backslash.
- Any other escape sequence is invalid and must fail before side effects.

## Behavioral Contract
1. Selector validation and unique-match resolution MUST happen before purge/fetch.
2. Command MUST snapshot targeted cache before purge.
3. Command MUST purge targeted cache and refetch (latest when no `--tag`, explicit when provided).
4. On success, command MUST update the targeted config reference pin to `(tag, commitSha)`.
5. On fetch failure after purge, command MUST restore previous snapshot and keep config unchanged.
6. On rollback failure, command MUST return non-zero and report both fetch and rollback failures.

## Exit Semantics
- `0`: Upgrade completed successfully and targeted reference updated.
- `6`: Selector validation/resolution failure.
- `7`: Fetch/config update execution failure.
- `8`: Rollback failure after fetch failure.

## Diagnostics Requirements
- Must state command mode: `latest-refresh` or `explicit-tag`.
- Must report targeted selector.
- On success, must report final `(tag, commitSha)`.
- On failure, must report actionable reason and whether rollback succeeded.
- On rollback failure, diagnostics must include both fetch and rollback error codes/messages.
Loading
Loading