Skip to content

extensibility enhancements (template and rule packs)#5

Merged
aabs merged 7 commits into
mainfrom
feature/extensibility-enhancments
May 21, 2026
Merged

extensibility enhancements (template and rule packs)#5
aabs merged 7 commits into
mainfrom
feature/extensibility-enhancments

Conversation

@aabs
Copy link
Copy Markdown
Owner

@aabs aabs commented May 20, 2026

Custom Template Packs and Rules Packs

Adds two pack-based extensibility mechanisms to Steergen and retires the legacy globalRoot configuration.

Summary

  • Template Packs — User-provided Scriban templates that override built-in rendering for specific targets, or provide complete target definitions for new external targets. Sourced from local directories or public GitHub repositories.
  • Rules Packs — Shared governance rule sets published to GitHub repositories, loaded and merged alongside project-local rules with scope-based precedence (project > supplemental > global).
  • globalRoot Retirement — The legacy filesystem path coupling is removed. Existing usage emits CFG001 and exits with code 2. A migration guide documents the conversion to rules packs.

What was done

Core infrastructure (tasks 1–10)

  • Pack manifest model, parser, and validation (PackManifest, PackManifestParser, PackScope, PackType)
  • PackDownloader with GitHub archive tarball download, atomic cache replacement, path traversal rejection, and SHA pinning detection
  • TemplateResolver implementing three-level override precedence (local > cached GitHub > built-in) with target-scoped filtering, 1 MB file size limit, and symlink rejection
  • RulesPackLoader with recursive .md discovery, scope-based merge, source tagging, and version compatibility checks
  • PackTargetComponent enabling external targets without dynamic plugin loading
  • TargetRegistry extension with RegisterPackTargets, IsAvailable, and ValidatePackRemoval
  • Extended SteeringConfiguration with TemplatePack and RulesPacks fields
  • globalRoot deprecation detection (CFG001)
  • 15 property-based tests (CsCheck) covering all correctness properties from the design

CLI commands (tasks 11–13)

  • steergen template-pack add/remove
  • steergen rules-pack add/remove/list
  • steergen update --templates / steergen update --rules (with --force for SHA-pinned packs)
  • steergen inspect --templates / steergen inspect --rules
  • steergen validate extended for template pack Scriban syntax validation
  • Centralized PackDownloaderFactory with shared HttpClient and branch-ref diagnostic warnings

Pipeline wiring (task 14)

  • TemplateResolver replaces direct EmbeddedTemplateProvider usage in the generation pipeline
  • RulesPackLoader feeds into extended SteeringResolver.Resolve with ScopedPackDocuments
  • TargetRegistrationService.AddAsync validates target availability via TargetRegistry.IsAvailable
  • TemplatePackService.RemoveAsync checks for TP010 (orphaned registered targets)

Integration tests (task 16)

  • 71 new integration tests covering template pack CLI, rules pack CLI, globalRoot deprecation, external target packs, and security (path traversal, file size limits, symlink rejection)

Documentation (tasks 18–19)

  • README updated with Template Packs and Rules Packs sections
  • Migration guide for globalRoot removal (docs/migration/globalroot-removal.md)
  • Diagnostic codes reference (docs/diagnostics.md)
  • Security misuse and abuse analysis (docs/security/pack-security-analysis.md)

Test results

761 tests passing: 289 unit + 253 integration + 219 property. Zero warnings, zero errors.

Breaking changes

  • globalRoot field removed from steergen.config.yaml. Existing configs using it will fail with CFG001 until migrated to rules packs.

mattana added 6 commits May 19, 2026 10:18
…pack loader

- Add default-branch resolution behavior: when `ref` is null, archive URL uses `HEAD`
- Add subdirectory extraction support: when `path` is specified, extract only that subdirectory's contents from archive
- Add HTTP error handling test requirement: DL001 diagnostic with HTTP status code and repository URL
- Add test cases for default-branch resolution and subdirectory extraction in PackDownloader tests
- Add `ScopedPackDocuments` record to rules pack configuration with Scope and Documents fields
- Add new checkpoint batch (id: 11) with tasks 18.1, 18.2, 18.3, 19.1
- Update requirement references in PackDownloader task to include 3.5 and 9.5
- Update requirement references in PackDownloader tests to include 3.3, 3.5, and 9.5
Copilot AI review requested due to automatic review settings May 20, 2026 09:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces pack-based extensibility to Steergen by adding Template Packs (override/extend Scriban rendering, including external targets) and Rules Packs (shared rule sets loaded from GitHub with scope-based precedence), while retiring the legacy globalRoot configuration (CFG001 error + exit code 2).

Changes:

  • Added GitHub pack source parsing, pack manifest parsing/validation, GitHub tarball download + caching, and pack security checks.
  • Implemented template resolution via a 3-layer TemplateResolver (local override > cached GitHub pack > embedded) and pack-provided targets via PackTargetComponent.
  • Added rules-pack loading/merge plumbing (scope-aware merge), new CLI commands/flags, and extensive unit/property/integration test coverage; removed globalRoot from config model and updated docs.

Reviewed changes

Copilot reviewed 93 out of 93 changed files in this pull request and generated 20 comments.

Show a summary per file
File Description
tests/Steergen.Core.UnitTests/Packs/GitHubPackSourceParserTests.cs Unit tests for github:owner/repo parsing/formatting.
tests/Steergen.Core.UnitTests/Configuration/TemplatePackServiceTests.cs Unit tests for template pack removal behavior.
tests/Steergen.Core.UnitTests/Configuration/TargetRegistrationConfigLockTests.cs Updates config-lock tests and adds unavailable-target add test.
tests/Steergen.Core.UnitTests/Configuration/OptimisticConfigWriterTests.cs Removes globalRoot assertions from config writer tests.
tests/Steergen.Core.UnitTests/Configuration/GlobalRootDeprecationTests.cs Unit tests for CFG001 detection.
tests/Steergen.Core.PropertyTests/Packs/VersionCompatibilityProperties.cs Property tests for pack version compatibility.
tests/Steergen.Core.PropertyTests/Packs/ShaDetectionProperties.cs Property tests for SHA pin detection.
tests/Steergen.Core.PropertyTests/Packs/PathTraversalProperties.cs Property tests for archive path traversal rejection.
tests/Steergen.Core.PropertyTests/Packs/FileSizeLimitProperties.cs Property tests for template file size limit enforcement.
tests/Steergen.Core.PropertyTests/Packs/FileDiscoveryProperties.cs Property tests for rules pack markdown discovery.
tests/Steergen.Core.PropertyTests/Packs/ConfigurationProperties.cs Property tests for YAML round-trip of pack config fields.
tests/Steergen.Core.PropertyTests/Packs/CachePathProperties.cs Property tests for cache path construction.
tests/Steergen.Core.PropertyTests/Merge/OverlayAndProfileProperties.cs Adjusts resolver calls to new signature usage patterns.
tests/Steergen.Cli.IntegrationTests/ValidateCommandTests.cs Adds integration coverage for template pack validation behavior.
tests/Steergen.Cli.IntegrationTests/UpdateCommandTests.cs Adds integration coverage for update --rules flows.
tests/Steergen.Cli.IntegrationTests/RunLayoutOverrideTests.cs Removes legacy layout override integration tests.
tests/Steergen.Cli.IntegrationTests/RunLayoutConventionsAcceptanceTests.cs Updates run tests for new root handling.
tests/Steergen.Cli.IntegrationTests/RunCatchAllRoutingTests.cs Updates routing tests for removed globalRoot config field behavior.
tests/Steergen.Cli.IntegrationTests/RunAndTargetCommandsTests.cs Updates run/target tests and resets registry between runs.
tests/Steergen.Cli.IntegrationTests/InspectCommandTests.cs Removes globalRoot usage in inspect tests.
tests/Steergen.Cli.IntegrationTests/InitCommandTests.cs Removes globalRoot expectation from init test.
tests/Steergen.Cli.IntegrationTests/GlobalRootDeprecationIntegrationTests.cs Integration tests for CFG001 behavior in run.
tests/Steergen.Cli.IntegrationTests/ConstitutionProvenanceTests.cs Removes globalRoot usage from provenance tests.
tests/Steergen.Benchmarks/ScalabilityEnvelopeBenchmarks.cs Updates resolver call sites to new signature patterns.
tests/CheckScriban/CheckScriban.csproj Adds a standalone Scriban check project (not in solution).
tests/check_scriban.csx Adds a Scriban parsing scratch script.
src/Steergen.Core/Targets/TemplateSource.cs Defines template source layer enum for inspection.
src/Steergen.Core/Targets/TemplateResolver.cs Implements layered template resolution + size/symlink checks.
src/Steergen.Core/Targets/TargetRegistry.cs Adds pack target registration, availability checks, removal validation.
src/Steergen.Core/Targets/TargetDescriptor.cs Extends descriptors with origin/pack metadata.
src/Steergen.Core/Targets/PackTargetComponent.cs Generic target component for pack-provided targets.
src/Steergen.Core/Steergen.Core.csproj Updates Scriban version and adds InternalsVisibleTo entries.
src/Steergen.Core/Packs/TemplatePackValidator.cs Adds Scriban syntax + template naming validation.
src/Steergen.Core/Packs/ScopedPackDocuments.cs Adds scope grouping model for scoped merge input.
src/Steergen.Core/Packs/RulesPackLoadResult.cs Adds rules pack load result model (docs + diagnostics).
src/Steergen.Core/Packs/RulesPackLoader.cs Implements rules pack load/validate/tagging pipeline.
src/Steergen.Core/Packs/RulesPackFileDiscovery.cs Adds shared markdown discovery helper (ordinal, no symlinks).
src/Steergen.Core/Packs/RulesPackConfiguration.cs Adds internal rules pack config model (source + scope override).
src/Steergen.Core/Packs/ProvidedTargetDefinition.cs Adds manifest model for pack-provided targets.
src/Steergen.Core/Packs/PackVersionChecker.cs Adds semver compatibility helper.
src/Steergen.Core/Packs/PackType.cs Adds pack type enum.
src/Steergen.Core/Packs/PackScope.cs Adds pack scope enum.
src/Steergen.Core/Packs/PackManifestParser.cs Adds manifest parsing + validation logic.
src/Steergen.Core/Packs/PackManifest.cs Adds manifest model.
src/Steergen.Core/Packs/PackDownloadResult.cs Adds download result model.
src/Steergen.Core/Packs/PackDownloader.cs Adds GitHub tar.gz download + extraction + caching.
src/Steergen.Core/Packs/GitHubPackSourceParser.cs Adds github:owner/repo notation parsing/formatting.
src/Steergen.Core/Packs/GitHubPackSource.cs Adds GitHub pack source model (owner/repo/ref/path).
src/Steergen.Core/Model/SteeringRule.cs Tags rules with pack provenance metadata.
src/Steergen.Core/Model/SteeringConfiguration.cs Removes globalRoot; adds template pack + rules packs config fields.
src/Steergen.Core/Model/ResolvedSteeringModel.cs Adds diagnostics output to resolved model.
src/Steergen.Core/Merge/SteeringResolver.cs Adds scope-aware merge overload for pack documents.
src/Steergen.Core/Generation/GenerationPipeline.cs Wires pack-aware resolver + pack target layout loading.
src/Steergen.Core/Configuration/TemplatePackService.cs Adds template pack remove service with orphan-target checks.
src/Steergen.Core/Configuration/TargetRegistrationService.cs Verifies target availability before registration.
src/Steergen.Core/Configuration/SteergenConfigWriter.cs Writes new pack config fields; removes globalRoot.
src/Steergen.Core/Configuration/SteergenConfigLoader.cs Loads new pack config fields; adds CFG001 deprecated field detection.
src/Steergen.Core/Configuration/RulesPackRegistrationService.cs Adds add/remove service for rules pack entries.
src/Steergen.Core/Configuration/LayoutOverrideLoader.cs Adds loading layouts from files (for pack targets).
src/Steergen.Cli/Composition/PackDownloaderFactory.cs Centralizes shared HttpClient + branch-ref warnings.
src/Steergen.Cli/Composition/CommandFactory.cs Registers new template-pack and rules-pack commands.
src/Steergen.Cli/Commands/ValidateCommand.cs Adds CFG001 detection and template pack validation.
src/Steergen.Cli/Commands/UpdateCommand.cs Adds update --templates/--rules/--force pack refresh flows.
src/Steergen.Cli/Commands/TemplatePackRemoveCommand.cs Implements template-pack remove.
src/Steergen.Cli/Commands/TemplatePackCommand.cs Adds template-pack parent command.
src/Steergen.Cli/Commands/TemplatePackAddCommand.cs Implements template-pack add (config + download).
src/Steergen.Cli/Commands/RunCommand.cs Wires template resolver, pack targets, rules pack loading, CFG001 check.
src/Steergen.Cli/Commands/RulesPackRemoveCommand.cs Implements rules-pack remove.
src/Steergen.Cli/Commands/RulesPackListCommand.cs Implements rules-pack list.
src/Steergen.Cli/Commands/RulesPackCommand.cs Adds rules-pack parent command.
src/Steergen.Cli/Commands/RulesPackAddCommand.cs Implements rules-pack add (config + download).
src/Steergen.Cli/Commands/PurgeCommand.cs Removes globalRoot reliance in purge flow.
src/Steergen.Cli/Commands/InspectCommand.cs Adds inspect --templates/--rules behaviors and pack inspection.
src/Steergen.Cli/Commands/InitCommand.cs Removes globalRoot from generated config.
README.md Documents template packs, rules packs, new commands, and globalRoot removal.
docs/migration/globalroot-removal.md Adds migration guide for globalRoot removal.
.kiro/specs/custom-template-packs/.config.kiro Adds spec metadata file.

Comment on lines +7 to +13
/// Subcommands: <c>template-pack remove</c>.
/// </summary>
public static class TemplatePackCommand
{
public static Command Create()
{
var cmd = new Command("template-pack", "Manage the template pack configuration");
Comment thread README.md
| `activeProfiles` | Profile names (legacy; retained for backward compatibility) |
| `templatePackVersion` | Template pack version (managed by `steergen update`) |

> **Note:** The `globalRoot` field has been removed. If your config still contains `globalRoot`, Steergen will report error CFG001 and exit with code 2. See the [migration guide](docs/migration-globalroot.md) for how to convert existing global rules to a rules pack.
Comment thread README.md
| `steergen template-pack add <source> [--ref <ref>] [--path <localPath>]` | Add a template pack |
| `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 remove <name>` | Remove a rules pack by name |
Comment on lines +198 to +203
// Parse the pack manifest to get declared targets
var manifest = manifestParser.Parse(packPath);
var declaredTargets = manifest?.Targets;
var registeredTargets = config.RegisteredTargets;

// Enumerate all .scriban files in the pack directory (deterministic order)
if (registeredTargets.Count > 0 && !registeredTargets.Contains(targetId))
{
diagnostics.Add(new Diagnostic(
"TP006",
Comment on lines +55 to +60
return a.Patch.CompareTo(b.Patch);
}

[GeneratedRegex(@"^(\d+)\.(\d+)\.(\d+)$", RegexOptions.Compiled)]
private static partial Regex SemverRegex();
}
var configDir = Path.GetDirectoryName(Path.GetFullPath(configPath))!;
localOverridePath = Path.GetFullPath(Path.Combine(configDir, localOverridePath));
}

var cacheBase = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
".steergen");
var downloader = new PackDownloader(new HttpClient(), cacheBase);
Comment on lines +263 to +276
// Resolve template pack configuration
var templatePackConfig = config?.TemplatePack;
string? localOverridePath = templatePackConfig?.LocalPath;
string? cachedPackPath = null;
IReadOnlySet<string>? declaredTargets = null;

if (templatePackConfig?.Source is not null)
{
var parsed = GitHubPackSourceParser.Parse(templatePackConfig.Source, templatePackConfig.Ref);
if (parsed is not null)
{
var cacheBase = GetCacheBaseDirectory();
var downloader = new PackDownloader(new HttpClient(), cacheBase);
cachedPackPath = downloader.GetCachedPath(parsed, PackType.Template);
Comment on lines +103 to +126
/// <summary>
/// Returns the source layer that would provide the template.
/// Used by <c>steergen inspect --templates</c>.
/// </summary>
public TemplateSource GetTemplateSource(string targetId, string templateName)
{
ArgumentException.ThrowIfNullOrEmpty(targetId);
ArgumentException.ThrowIfNullOrEmpty(templateName);

var targetInScope = IsTargetInScope(targetId);

// Layer 1: Local override path
if (targetInScope && _localOverridePath is not null)
{
if (TemplateFileExists(_localOverridePath, targetId, templateName))
return TemplateSource.LocalOverride;
}

// Layer 2: Cached GitHub pack
if (targetInScope && _cachedPackPath is not null)
{
if (TemplateFileExists(_cachedPackPath, targetId, templateName))
return TemplateSource.CachedGitHubPack;
}
…section

refactor: improve top-level prefix detection in PackDownloader
test: add unit test for handling metadata entries before top-level directory in PackDownloader
@aabs aabs merged commit 9a852c6 into main May 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants