extensibility enhancements (template and rule packs)#5
Merged
Conversation
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
Contributor
There was a problem hiding this comment.
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 viaPackTargetComponent. - Added rules-pack loading/merge plumbing (scope-aware merge), new CLI commands/flags, and extensive unit/property/integration test coverage; removed
globalRootfrom 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"); |
| | `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. |
| | `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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Custom Template Packs and Rules Packs
Adds two pack-based extensibility mechanisms to Steergen and retires the legacy
globalRootconfiguration.Summary
globalRootRetirement — 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)
PackManifest,PackManifestParser,PackScope,PackType)PackDownloaderwith GitHub archive tarball download, atomic cache replacement, path traversal rejection, and SHA pinning detectionTemplateResolverimplementing three-level override precedence (local > cached GitHub > built-in) with target-scoped filtering, 1 MB file size limit, and symlink rejectionRulesPackLoaderwith recursive.mddiscovery, scope-based merge, source tagging, and version compatibility checksPackTargetComponentenabling external targets without dynamic plugin loadingTargetRegistryextension withRegisterPackTargets,IsAvailable, andValidatePackRemovalSteeringConfigurationwithTemplatePackandRulesPacksfieldsglobalRootdeprecation detection (CFG001)CLI commands (tasks 11–13)
steergen template-pack add/removesteergen rules-pack add/remove/liststeergen update --templates/steergen update --rules(with--forcefor SHA-pinned packs)steergen inspect --templates/steergen inspect --rulessteergen validateextended for template pack Scriban syntax validationPackDownloaderFactorywith sharedHttpClientand branch-ref diagnostic warningsPipeline wiring (task 14)
TemplateResolverreplaces directEmbeddedTemplateProviderusage in the generation pipelineRulesPackLoaderfeeds into extendedSteeringResolver.ResolvewithScopedPackDocumentsTargetRegistrationService.AddAsyncvalidates target availability viaTargetRegistry.IsAvailableTemplatePackService.RemoveAsyncchecks for TP010 (orphaned registered targets)Integration tests (task 16)
Documentation (tasks 18–19)
globalRootremoval (docs/migration/globalroot-removal.md)docs/diagnostics.md)docs/security/pack-security-analysis.md)Test results
761 tests passing: 289 unit + 253 integration + 219 property. Zero warnings, zero errors.
Breaking changes
globalRootfield removed fromsteergen.config.yaml. Existing configs using it will fail with CFG001 until migrated to rules packs.