<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Design on scafctl</title><link>https://oakwood-commons.github.io/scafctl/docs/design/</link><description>Recent content in Design on scafctl</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://oakwood-commons.github.io/scafctl/docs/design/index.xml" rel="self" type="application/rss+xml"/><item><title>Solutions</title><link>https://oakwood-commons.github.io/scafctl/docs/design/solutions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/solutions/</guid><description>&lt;h1 id="solutions"&gt;Solutions&lt;a class="anchor" href="#solutions"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="implementation-status"&gt;Implementation Status&lt;a class="anchor" href="#implementation-status"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Feature&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Solution structure (apiVersion/kind/metadata/spec)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/solution.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Metadata fields&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Includes icon/banner beyond original design&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Catalog fields&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;visibility, beta, disabled&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Spec with resolvers&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/spec.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Workflow with actions/finally&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Uses &lt;code&gt;workflow.actions&lt;/code&gt; and &lt;code&gt;workflow.finally&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Dependencies (plugins)&lt;/td&gt;
 &lt;td&gt;⏳ Planned&lt;/td&gt;
 &lt;td&gt;Declared under &lt;code&gt;bundle.plugins&lt;/code&gt; — see &lt;a href="https://oakwood-commons.github.io/scafctl/docs/design/catalog-build-bundling/"&gt;catalog-build-bundling.md&lt;/a&gt;
&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Validation&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/spec_validation.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Run command&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;scafctl run solution&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Render command&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;scafctl render solution&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A solution is the top-level unit of configuration in scafctl. It defines &lt;em&gt;what exists&lt;/em&gt;, &lt;em&gt;how data is obtained&lt;/em&gt;, and &lt;em&gt;what actions should occur&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>Resolvers</title><link>https://oakwood-commons.github.io/scafctl/docs/design/resolvers/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/resolvers/</guid><description>&lt;h1 id="resolvers"&gt;Resolvers&lt;a class="anchor" href="#resolvers"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Resolvers produce named data values. They exist to gather, normalize, validate, and emit data in a deterministic way so that actions and other resolvers can consume it without re-computation or implicit behavior.&lt;/p&gt;
&lt;p&gt;Resolvers are the only mechanism for introducing data into a solution. Actions never fetch or derive data on their own.&lt;/p&gt;
&lt;p&gt;Resolvers do not cause side effects. They only compute values.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="implementation-status"&gt;Implementation Status&lt;a class="anchor" href="#implementation-status"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Feature&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;th&gt;Location&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Resolver struct (Name, Description, Type, When, etc.)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/resolver.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Resolver Context (&lt;code&gt;sync.Map&lt;/code&gt;, thread-safe)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/context.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ValueRef (Literal, Resolver, Expr, Tmpl)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/spec/valueref.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Phase-based execution (DAG ordering)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/phase.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Dependency extraction (CEL, templates, &lt;code&gt;dependsOn&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/graph.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cycle detection&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Uses &lt;code&gt;pkg/dag&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Type coercion (string, int, float, bool, array, object, any)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/spec/types.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Additional types: &lt;code&gt;time&lt;/code&gt;, &lt;code&gt;duration&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/spec/types.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Special symbols (&lt;code&gt;__self&lt;/code&gt;, &lt;code&gt;__item&lt;/code&gt;, &lt;code&gt;__index&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/executor.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Iteration aliases (&lt;code&gt;item&lt;/code&gt;, &lt;code&gt;index&lt;/code&gt; in forEach)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/spec/foreach.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Error handling (ExecutionError, AggregatedValidationError)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/errors.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Redaction for sensitive values&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;RedactedError&lt;/code&gt;, snapshots&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Timeout configuration (resolver, phase, default)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ExecutorOption&lt;/code&gt; functions&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Concurrency control (&lt;code&gt;maxConcurrency&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;WithMaxConcurrency()&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Progress callbacks&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ProgressCallback&lt;/code&gt; interface&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Snapshots&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/snapshot.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Graph visualization (DOT, Mermaid, ASCII, JSON)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/graph.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Prometheus metrics&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/metrics.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;forEach in transform&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ForEachClause&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;forEach &lt;code&gt;keepSkipped&lt;/code&gt; (nil retention opt-in)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ForEachClause.KeepSkipped&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;forEach nil filtering (default behavior)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/resolver/executor.go&lt;/code&gt;, &lt;code&gt;pkg/spec/foreach.go&lt;/code&gt; (&lt;code&gt;KeepSkipped&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;onError&lt;/code&gt; behavior&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ErrorBehavior&lt;/code&gt; type&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ValidateAll mode (&lt;code&gt;--validate-all&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;WithValidateAll()&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SkipValidation mode (&lt;code&gt;--skip-validation&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;WithSkipValidation()&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Value size limits&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;WarnValueSize&lt;/code&gt;, &lt;code&gt;MaxValueSize&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Run resolver command&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/cmd/scafctl/run/resolver.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="responsibilities"&gt;Responsibilities&lt;a class="anchor" href="#responsibilities"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A resolver is responsible for:&lt;/p&gt;</description></item><item><title>Providers</title><link>https://oakwood-commons.github.io/scafctl/docs/design/providers/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/providers/</guid><description>&lt;h1 id="providers"&gt;Providers&lt;a class="anchor" href="#providers"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Providers are stateless execution primitives. They perform a single, well-defined operation given validated inputs and return either a result or an error.&lt;/p&gt;
&lt;p&gt;Providers do not own orchestration, control flow, dependency resolution, or lifecycle decisions. This separation keeps solutions deterministic, testable, and explicit.&lt;/p&gt;
&lt;p&gt;Providers are used by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resolvers (during resolve, transform, and validate phases)&lt;/li&gt;
&lt;li&gt;Actions (during execution or render)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Providers are never invoked implicitly. A provider runs only when explicitly referenced.&lt;/p&gt;</description></item><item><title>Actions</title><link>https://oakwood-commons.github.io/scafctl/docs/design/actions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/actions/</guid><description>&lt;h1 id="actions"&gt;Actions&lt;a class="anchor" href="#actions"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Actions describe side effects as a declarative execution graph. They exist to model what should be done, not how data is derived.&lt;/p&gt;
&lt;p&gt;Actions consume resolved data, declare dependencies, and reference results from other actions in a structured way. Actions may be executed directly by scafctl or rendered for execution by another system.&lt;/p&gt;
&lt;p&gt;Resolvers compute data.
Actions perform work.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="responsibilities"&gt;Responsibilities&lt;a class="anchor" href="#responsibilities"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An action is responsible for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Declaring an executable operation&lt;/li&gt;
&lt;li&gt;Selecting a provider&lt;/li&gt;
&lt;li&gt;Declaring dependencies on other actions&lt;/li&gt;
&lt;li&gt;Consuming resolver values and action results&lt;/li&gt;
&lt;li&gt;Defining execution conditions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An action is not responsible for:&lt;/p&gt;</description></item><item><title>Authentication</title><link>https://oakwood-commons.github.io/scafctl/docs/design/auth/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/auth/</guid><description>&lt;h1 id="authentication-and-authorization"&gt;Authentication and Authorization&lt;a class="anchor" href="#authentication-and-authorization"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Authentication in scafctl is declarative, provider-driven, and execution-agnostic.&lt;/p&gt;
&lt;p&gt;Providers declare what kind of token they require. scafctl or an external executor decides how that token is obtained and supplied. For local users, scafctl can initiate interactive authentication flows. For APIs and workflow engines, credentials are injected explicitly.&lt;/p&gt;
&lt;p&gt;Authentication is a system concern, not a provider implementation detail.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="terminology"&gt;Terminology&lt;a class="anchor" href="#terminology"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auth Handler&lt;/strong&gt;: A component that implements the &lt;code&gt;auth.Handler&lt;/code&gt; interface and manages identity verification, credential storage, and token acquisition for a specific identity provider (e.g., Entra, GitHub). Auth handlers are registered in the auth registry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auth Provider&lt;/strong&gt; (in provider inputs): The &lt;code&gt;authProvider&lt;/code&gt; field in HTTP provider inputs that specifies which auth handler to use for a request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provider&lt;/strong&gt;: Action/resolver providers that perform work (e.g., HTTP, shell, file). Distinct from auth handlers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auth Handler Artifact&lt;/strong&gt;: A go-plugin binary distributed via the catalog that exposes one or more auth handlers.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="built-in-vs-external-auth-handlers"&gt;Built-in vs External Auth Handlers&lt;a class="anchor" href="#built-in-vs-external-auth-handlers"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;scafctl provides built-in auth handlers for common identity providers:&lt;/p&gt;</description></item><item><title>CEL Integration</title><link>https://oakwood-commons.github.io/scafctl/docs/design/cel/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/cel/</guid><description>&lt;h1 id="cel-integration-architecture"&gt;CEL Integration Architecture&lt;a class="anchor" href="#cel-integration-architecture"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This document describes the architecture and best practices for integrating Common Expression Language (CEL) into scafctl. The design emphasizes performance through intelligent caching, thread safety through isolated execution contexts, and correctness through dependency-based execution ordering.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="architecture-principles"&gt;Architecture Principles&lt;a class="anchor" href="#architecture-principles"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="single-global-cel-environment"&gt;Single Global CEL Environment&lt;a class="anchor" href="#single-global-cel-environment"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One CEL environment per application instance&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Created at application startup&lt;/li&gt;
&lt;li&gt;Contains all built-in and custom extension functions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never recreated during runtime&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Shared across all requests and commands&lt;/li&gt;
&lt;li&gt;Enables effective AST caching&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>GitHub Auth Handler</title><link>https://oakwood-commons.github.io/scafctl/docs/design/github-auth-handler/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/github-auth-handler/</guid><description>&lt;h1 id="github-auth-handler-implementation-plan"&gt;GitHub Auth Handler Implementation Plan&lt;a class="anchor" href="#github-auth-handler-implementation-plan"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Implement a builtin GitHub auth handler (&lt;code&gt;github&lt;/code&gt;) following the established patterns from the Entra handler. The handler supports four authentication flows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Interactive (OAuth Authorization Code + PKCE)&lt;/strong&gt; — Browser-based login for local development (default)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Device Code&lt;/strong&gt; — Headless/SSH fallback using OAuth device authorization grant&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PAT&lt;/strong&gt; — Personal Access Token from environment variables for CI/CD&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub App&lt;/strong&gt; — Installation token for service-to-service automation&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="design-decisions"&gt;Design Decisions&lt;a class="anchor" href="#design-decisions"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="authentication-flows"&gt;Authentication Flows&lt;a class="anchor" href="#authentication-flows"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Flow&lt;/th&gt;
 &lt;th&gt;Use Case&lt;/th&gt;
 &lt;th&gt;Mechanism&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Interactive&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Local development (default)&lt;/td&gt;
 &lt;td&gt;OAuth 2.0 Authorization Code + PKCE via browser redirect&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Device Code&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Headless / SSH environments&lt;/td&gt;
 &lt;td&gt;OAuth 2.0 device authorization grant via GitHub OAuth App&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;PAT&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;CI/CD pipelines, automation&lt;/td&gt;
 &lt;td&gt;Read &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; or &lt;code&gt;GH_TOKEN&lt;/code&gt; from environment variables&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;GitHub App&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Service-to-service automation&lt;/td&gt;
 &lt;td&gt;JWT → installation access token via GitHub App credentials&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Rationale&lt;/strong&gt;: Interactive (browser) flow is the modern standard for CLI tools (&lt;code&gt;gh&lt;/code&gt;, &lt;code&gt;az&lt;/code&gt;, &lt;code&gt;gcloud&lt;/code&gt; all use it as their default). Device code is the fallback for headless environments. PAT from environment mirrors the Entra handler&amp;rsquo;s service principal pattern and aligns with GitHub Actions&amp;rsquo; &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; injection. GitHub App flow enables automated workflows that need repository access without a user context.&lt;/p&gt;</description></item><item><title>Catalog</title><link>https://oakwood-commons.github.io/scafctl/docs/design/catalog/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/catalog/</guid><description>&lt;h1 id="scafctl-catalog-system"&gt;scafctl Catalog System&lt;a class="anchor" href="#scafctl-catalog-system"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;scafctl manages artifact lifecycle through a workflow analogous to container images, using OCI artifacts for storage and distribution. This design enables version control, dependency management, and consistent deployment across environments for solutions, providers, auth handlers, and other scafctl artifacts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="core-concepts"&gt;Core Concepts&lt;a class="anchor" href="#core-concepts"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="artifacts"&gt;Artifacts&lt;a class="anchor" href="#artifacts"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The catalog system supports multiple artifact types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Solutions&lt;/strong&gt;: YAML configuration files that define resolvers, actions, and dependencies&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Providers&lt;/strong&gt;: Binary executables that provide custom providers using hashicorp/go-plugin over gRPC&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auth Handlers&lt;/strong&gt;: Binary executables that provide authentication handlers using hashicorp/go-plugin over gRPC&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future artifact types&lt;/strong&gt;: TBD as the system evolves&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="catalogs"&gt;Catalogs&lt;a class="anchor" href="#catalogs"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Local catalog&lt;/strong&gt;: Functions like Docker&amp;rsquo;s local image store, caching built and pulled artifacts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote catalog&lt;/strong&gt;: Centralized OCI registry with GUI frontend for artifact discovery and distribution&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="artifact-identification"&gt;Artifact Identification&lt;a class="anchor" href="#artifact-identification"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Artifacts are distinguished using OCI media types and annotations:&lt;/p&gt;</description></item><item><title>GCP Auth Handler</title><link>https://oakwood-commons.github.io/scafctl/docs/design/gcp-auth-handler/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/gcp-auth-handler/</guid><description>&lt;h1 id="gcp-auth-handler-implementation-plan"&gt;GCP Auth Handler Implementation Plan&lt;a class="anchor" href="#gcp-auth-handler-implementation-plan"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Implement a builtin GCP auth handler (&lt;code&gt;gcp&lt;/code&gt;) following the established patterns from the Entra and GitHub handlers. The handler will support four authentication flows: Application Default Credentials (ADC) for interactive use, Service Account Key for CI/CD, Workload Identity Federation for GKE/cross-cloud, and GCE Metadata Server for cloud-hosted workloads. Service account impersonation will be supported across all flows.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="design-decisions"&gt;Design Decisions&lt;a class="anchor" href="#design-decisions"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="authentication-flows"&gt;Authentication Flows&lt;a class="anchor" href="#authentication-flows"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Flow&lt;/th&gt;
 &lt;th&gt;Use Case&lt;/th&gt;
 &lt;th&gt;Mechanism&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;ADC (Application Default Credentials)&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Interactive CLI use&lt;/td&gt;
 &lt;td&gt;OAuth 2.0 authorization code + PKCE via browser, with gcloud ADC fallback&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Service Account Key&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;CI/CD pipelines, automation&lt;/td&gt;
 &lt;td&gt;JWT assertion from JSON key file via &lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Workload Identity Federation&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;GKE, cross-cloud (AWS/Azure/OIDC)&lt;/td&gt;
 &lt;td&gt;STS token exchange for federated external tokens&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Metadata Server&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;GCE, Cloud Run, GKE&lt;/td&gt;
 &lt;td&gt;Token from &lt;code&gt;http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Flow auto-detection priority&lt;/strong&gt;: Workload Identity Federation &amp;gt; Metadata Server &amp;gt; Service Account Key &amp;gt; ADC (stored credentials).&lt;/p&gt;</description></item><item><title>CLI</title><link>https://oakwood-commons.github.io/scafctl/docs/design/cli/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/cli/</guid><description>&lt;h1 id="cli-usage"&gt;CLI Usage&lt;a class="anchor" href="#cli-usage"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document describes how to invoke scafctl from the command line. The CLI follows a kubectl-style structure where verbs, kinds, and names are explicit and positional.&lt;/p&gt;
&lt;p&gt;The general pattern is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl &amp;lt;verb&amp;gt; &amp;lt;kind&amp;gt; &amp;lt;name[@version(or constraint)]&amp;gt; [flags]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;verb&amp;gt;&lt;/code&gt; describes what you want to do&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;kind&amp;gt;&lt;/code&gt; identifies the type of object&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;name&amp;gt;&lt;/code&gt; identifies the object&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@version&lt;/code&gt; is optional and resolved via the catalog ( or constraint)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="implementation-status"&gt;Implementation Status&lt;a class="anchor" href="#implementation-status"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Command&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;run solution&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Requires workflow (errors if no workflow defined; use &lt;code&gt;run resolver&lt;/code&gt; for resolver-only)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;run resolver&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Resolver-only execution for debugging and inspection&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;render solution&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Includes action-graph and snapshot modes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;get solution/provider/resolver&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;explain solution/provider&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;config *&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;view, get, set, unset, add-catalog, remove-catalog, use-catalog, init, schema, validate&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;snapshot show/diff&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;solution diff&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Structural comparison of two solution files&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;secrets *&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;list, get, set, delete, exists, export, import, rotate&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;auth *&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;login, logout, status, token&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;resolver graph&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;❌ Removed&lt;/td&gt;
 &lt;td&gt;Use &lt;code&gt;run resolver --graph&lt;/code&gt; instead&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;build solution&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Catalog feature&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;catalog list/inspect/delete/prune&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Catalog management&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;catalog save/load&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Offline distribution&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;eval cel&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Evaluate CEL expressions from CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;eval template&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Evaluate Go templates from CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;eval validate&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Validate solution files from CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;new solution&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Scaffold a new solution from template&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;lint rules&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;List all available lint rules&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;lint explain&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Explain a specific lint rule&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;examples list&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;List available example configurations&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;examples get&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Get/download an example file&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;push solution/plugin&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;📋 Planned&lt;/td&gt;
 &lt;td&gt;Remote catalog feature&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;pull solution/plugin&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;📋 Planned&lt;/td&gt;
 &lt;td&gt;Remote catalog feature&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;tag solution/plugin&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;📋 Planned&lt;/td&gt;
 &lt;td&gt;Catalog feature&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--catalog&lt;/code&gt; flag&lt;/td&gt;
 &lt;td&gt;📋 Planned&lt;/td&gt;
 &lt;td&gt;Catalog feature&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Version constraints (&lt;code&gt;@^1.2&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;📋 Planned&lt;/td&gt;
 &lt;td&gt;Requires catalog&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="core-concepts"&gt;Core Concepts&lt;a class="anchor" href="#core-concepts"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="kinds"&gt;Kinds&lt;a class="anchor" href="#kinds"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;solution&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;provider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resolver&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;catalog&lt;/code&gt; &lt;em&gt;(planned)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="names-and-versions"&gt;Names and Versions&lt;a class="anchor" href="#names-and-versions"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Names identify an object within a kind.&lt;/p&gt;</description></item><item><title>Plugins</title><link>https://oakwood-commons.github.io/scafctl/docs/design/plugins/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/plugins/</guid><description>&lt;h1 id="plugins"&gt;Plugins&lt;a class="anchor" href="#plugins"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Plugins are the extension mechanism for scafctl. They allow external binaries to contribute functionality to the system in a controlled, versioned, and discoverable way.&lt;/p&gt;
&lt;p&gt;The primary purpose of plugins is to supply providers and auth handlers. &amp;ldquo;Plugin&amp;rdquo; is an internal implementation term - users interact with &amp;ldquo;providers&amp;rdquo; and &amp;ldquo;auth handlers&amp;rdquo; as catalog artifact kinds.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="terminology"&gt;Terminology&lt;a class="anchor" href="#terminology"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Plugin&lt;/strong&gt;: Internal term for a go-plugin binary. Not exposed to users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provider Artifact&lt;/strong&gt;: A plugin binary distributed via the catalog that exposes one or more providers. Users push/pull &amp;ldquo;providers&amp;rdquo; not &amp;ldquo;plugins&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auth Handler Artifact&lt;/strong&gt;: A plugin binary distributed via the catalog that exposes one or more auth handlers.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="what-a-plugin-is"&gt;What a Plugin Is&lt;a class="anchor" href="#what-a-plugin-is"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A plugin is an external process that implements one or more scafctl extension interfaces and communicates with scafctl over RPC.&lt;/p&gt;</description></item><item><title>Testing</title><link>https://oakwood-commons.github.io/scafctl/docs/design/testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/testing/</guid><description>&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Testing in scafctl ensures that solutions behave correctly, deterministically, and safely before performing side effects.&lt;/p&gt;
&lt;p&gt;scafctl separates data resolution, transformation, and execution. Solution-level testing requires no mocks — render mode produces a fully concrete execution plan without side effects. At the package level, providers and services use test doubles to isolate external dependencies.&lt;/p&gt;
&lt;p&gt;Testing is an outcome of the design, not a separate subsystem.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="core-principles"&gt;Core Principles&lt;a class="anchor" href="#core-principles"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;All data enters through resolvers&lt;/li&gt;
&lt;li&gt;All computation is provider-backed&lt;/li&gt;
&lt;li&gt;All side effects are isolated to actions&lt;/li&gt;
&lt;li&gt;Render mode produces a fully concrete execution plan&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because of this:&lt;/p&gt;</description></item><item><title>Miscellaneous</title><link>https://oakwood-commons.github.io/scafctl/docs/design/misc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/misc/</guid><description>&lt;h1 id="miscellaneous-design-considerations"&gt;Miscellaneous Design Considerations&lt;a class="anchor" href="#miscellaneous-design-considerations"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document captures cross-cutting concepts that are intentionally not core primitives, but are required to make scafctl safe, predictable, and operable at scale.&lt;/p&gt;
&lt;p&gt;These concerns apply across resolvers, actions, providers, plugins, and solutions.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="implementation-status"&gt;Implementation Status&lt;a class="anchor" href="#implementation-status"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Concern&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Schema and Typing Model&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Provider &lt;code&gt;Descriptor&lt;/code&gt; with &lt;code&gt;Schema&lt;/code&gt; and &lt;code&gt;OutputSchemas&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Provider Contracts&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Full descriptor with input/output schemas, capabilities&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Provider Capabilities&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;from&lt;/code&gt;, &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;validation&lt;/code&gt;, &lt;code&gt;authentication&lt;/code&gt;, &lt;code&gt;action&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Secrets and Sensitive Data&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/secrets&lt;/code&gt; with AES-256-GCM, keychain integration&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Secret Redaction&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;RedactedError&lt;/code&gt;, &lt;code&gt;--redact&lt;/code&gt; flag on snapshots&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Stateless Core&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Providers are stateless, no persistent state&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Error Model&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Rich error types with location, cause, context&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Determinism Rules&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Resolver purity enforced, &lt;code&gt;WhatIf&lt;/code&gt; functions declared&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Resolver DAG Visualization&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;ASCII, DOT, Mermaid, JSON formats&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Action DAG Visualization&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;ASCII, DOT, Mermaid, JSON formats via &lt;code&gt;--action-graph&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Validation&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Schema, dependency, type validation&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Linting&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;scafctl lint&lt;/code&gt; with error/warning/info severities&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Extensibility (Providers/Plugins)&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;Plugin system for external providers&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Render vs Run Separation&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;scafctl render&lt;/code&gt; vs &lt;code&gt;scafctl run&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Dry-run Mode&lt;/td&gt;
 &lt;td&gt;✅ Implemented&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;--dry-run&lt;/code&gt; flag, &lt;code&gt;WhatIf&lt;/code&gt; on Descriptor, &lt;code&gt;DryRunFromContext()&lt;/code&gt; for provider-level&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="schema-and-typing-model"&gt;Schema and Typing Model&lt;a class="anchor" href="#schema-and-typing-model"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote class='book-hint '&gt;
&lt;p&gt;&lt;strong&gt;Status&lt;/strong&gt;: ✅ Implemented in &lt;code&gt;pkg/provider/provider.go&lt;/code&gt;&lt;/p&gt;</description></item><item><title>CLI Contributing</title><link>https://oakwood-commons.github.io/scafctl/docs/design/cli-contributing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/cli-contributing/</guid><description>&lt;h1 id="cli-implementation-guide"&gt;CLI Implementation Guide&lt;a class="anchor" href="#cli-implementation-guide"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document describes how to implement CLI commands in scafctl. It provides patterns, code examples, and best practices based on the existing codebase.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;a class="anchor" href="#table-of-contents"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#architecture-overview"&gt;Architecture Overview&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#package-structure"&gt;Package Structure&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#creating-a-new-command"&gt;Creating a New Command&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#command-components"&gt;Command Components&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#terminal-output"&gt;Terminal Output&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#data-output-with-kvx"&gt;Data Output with kvx&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#flags-and-parameters"&gt;Flags and Parameters&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#context-management"&gt;Context Management&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#testing-commands"&gt;Testing Commands&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#common-patterns"&gt;Common Patterns&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#checklist"&gt;Checklist&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="architecture-overview"&gt;Architecture Overview&lt;a class="anchor" href="#architecture-overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The scafctl CLI follows a kubectl-style command structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl &amp;lt;verb&amp;gt; &amp;lt;kind&amp;gt; &amp;lt;name[@version]&amp;gt; [flags]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The CLI is built using &lt;a href="https://github.com/spf13/cobra" target="_blank" rel="noopener noreferrer"&gt;Cobra&lt;/a&gt;
 and organized into a hierarchical command tree:&lt;/p&gt;</description></item><item><title>Functional Testing</title><link>https://oakwood-commons.github.io/scafctl/docs/design/functional-testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/functional-testing/</guid><description>&lt;h1 id="functional-testing"&gt;Functional Testing&lt;a class="anchor" href="#functional-testing"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Functional testing validates that solutions behave correctly by executing scafctl commands against them and asserting on the output. Solution authors define test cases inline in their solution spec. The &lt;code&gt;scafctl test functional&lt;/code&gt; command discovers tests, sets up isolated sandboxes, runs builtin and user-defined tests, and reports results.&lt;/p&gt;
&lt;p&gt;This is the primary mechanism for validating solutions in CI and during development.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="implementation-status"&gt;Implementation Status&lt;a class="anchor" href="#implementation-status"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Feature&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;test functional&lt;/code&gt; CLI command&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/cmd/scafctl/test/functional.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;test list&lt;/code&gt; CLI subcommand&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/cmd/scafctl/test/list.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Test spec types&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/soltesting/types.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Builtin tests&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Parse, resolve, render, lint in &lt;code&gt;builtins.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Command-based test execution&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;In-process cobra execution via &lt;code&gt;CommandBuilder&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CEL assertions&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/soltesting/assertions.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Regex assertions&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Contains assertions&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Negation assertions&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;notContains&lt;/code&gt;, &lt;code&gt;notRegex&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Golden file snapshots&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/soltesting/snapshot.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Init scripts (exec provider)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;InitStep&lt;/code&gt; with exec provider schema&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Test file includes&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;TestInclude&lt;/code&gt; discovery source in bundler&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Temp directory sandbox&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/soltesting/sandbox.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;JUnit XML reporting&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/soltesting/junit.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Compose support for tests&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;mergeTests()&lt;/code&gt; and &lt;code&gt;mergeTestConfig()&lt;/code&gt; in compose&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Parallel test execution&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Semaphore-based concurrency control&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CEL assertion diagnostics&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/soltesting/diagnostics.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Suite-level setup&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;testing.config.setup&lt;/code&gt; with base sandbox copy&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Test tags and filtering&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;--tag&lt;/code&gt; flag, &lt;code&gt;tags&lt;/code&gt; field on test cases&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Per-test environment variables&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;env&lt;/code&gt; field on test cases&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cleanup steps&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;cleanup&lt;/code&gt; field, runs even on failure&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Test inheritance (extends)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/soltesting/inheritance.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Assertion target (stderr)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;target&lt;/code&gt; field: &lt;code&gt;stdout&lt;/code&gt;, &lt;code&gt;stderr&lt;/code&gt;, &lt;code&gt;combined&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;File assertions (&lt;code&gt;__files&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Diff-based sandbox file change detection&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Fail-fast (per-solution)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;--fail-fast&lt;/code&gt; stops remaining tests per solution&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Test name validation&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Enforced in &lt;code&gt;TestCase.Validate()&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Selective builtin skip&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;SkipBuiltinsValue&lt;/code&gt; with custom unmarshal&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;In-process command execution&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Root()&lt;/code&gt; with &lt;code&gt;*RootOptions&lt;/code&gt;, &lt;code&gt;CommandBuilder&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Concurrency control&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-j&lt;/code&gt; flag, &lt;code&gt;--sequential&lt;/code&gt; as sugar for &lt;code&gt;-j 1&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Conditional skip (CEL &lt;code&gt;skip&lt;/code&gt; expression)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;CEL-based runtime skip evaluation&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Test retries&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;retries&lt;/code&gt; field for flaky test resilience&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Suite-level cleanup&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;testing.config.cleanup&lt;/code&gt; for teardown after all tests&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;File size guard&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Cap &lt;code&gt;files[].content&lt;/code&gt; at 10MB to prevent OOM&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;In-process execution safety&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Root()&lt;/code&gt; accepts &lt;code&gt;*RootOptions&lt;/code&gt;, no package-level state&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Unused template lint warning&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;unused-template&lt;/code&gt; lint rule&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Solution filtering (&lt;code&gt;--solution&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Glob-based solution name filtering&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--filter&lt;/code&gt; solution/test format&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;--filter &amp;quot;solution/test-name&amp;quot;&lt;/code&gt; glob support&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--dry-run&lt;/code&gt; flag&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Validate test definitions without executing&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Suite-level &lt;code&gt;env&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;testing.config.env&lt;/code&gt; shared across all tests&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Binary file content guard&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Non-UTF-8 files get &lt;code&gt;content&lt;/code&gt; set to &lt;code&gt;&amp;quot;&amp;lt;binary file&amp;gt;&amp;quot;&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Test execution ordering&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Alphabetical by name; builtins first&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Field max limits&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;assertions: 100&lt;/code&gt;, &lt;code&gt;files: 50&lt;/code&gt;, &lt;code&gt;tags: 20&lt;/code&gt;, extends depth: 10&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Glob zero-match error&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Test &lt;code&gt;files&lt;/code&gt; globs matching zero files produce &lt;code&gt;error&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Environment precedence chain&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;process → &lt;code&gt;testing.config.env&lt;/code&gt; → &lt;code&gt;TestCase.env&lt;/code&gt; → &lt;code&gt;InitStep.env&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;TestCase.Validate()&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Comprehensive test case validation method&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Extends non-existent error&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;extends&lt;/code&gt; referencing non-existent test names is a parse-time error&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Tests per solution limit&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;Max 500 tests per solution&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Watch mode (&lt;code&gt;--watch&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;fsnotify&lt;/code&gt;-based file watcher in &lt;code&gt;soltesting/watch.go&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Auto-generated tests (&lt;code&gt;-o test&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;✅ Done&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pkg/solution/soltesting/generate.go&lt;/code&gt;; wired into &lt;code&gt;render solution&lt;/code&gt;, &lt;code&gt;run resolver&lt;/code&gt;, &lt;code&gt;run solution&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="test-definition"&gt;Test Definition&lt;a class="anchor" href="#test-definition"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="location"&gt;Location&lt;a class="anchor" href="#location"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tests are defined under &lt;code&gt;spec.testing.cases&lt;/code&gt; in the solution YAML. Like resolvers, tests support the &lt;code&gt;compose&lt;/code&gt; mechanism for splitting into separate files.&lt;/p&gt;</description></item><item><title>Future Enhancements</title><link>https://oakwood-commons.github.io/scafctl/docs/design/future-enhancements/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/future-enhancements/</guid><description>&lt;h1 id="future-enhancements"&gt;Future Enhancements&lt;a class="anchor" href="#future-enhancements"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Consolidated index of planned features and future enhancements across all design docs. Each entry links back to the source design doc for full context.&lt;/p&gt;
&lt;p&gt;Items marked ✅ have been implemented since this document was originally written.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="actions"&gt;Actions&lt;a class="anchor" href="#actions"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Source: &lt;a href="https://oakwood-commons.github.io/scafctl/docs/design/actions/"&gt;actions.md&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id="-result-schema-validation"&gt;✅ Result Schema Validation&lt;a class="anchor" href="#-result-schema-validation"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Implemented.&lt;/strong&gt; Actions can declare a &lt;code&gt;resultSchema&lt;/code&gt; with JSON Schema and a &lt;code&gt;resultSchemaMode&lt;/code&gt; (&lt;code&gt;error&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;ignore&lt;/code&gt;). Validated at runtime in the action executor. Supports workflow-level defaults. See &lt;code&gt;pkg/action/result_validation.go&lt;/code&gt; and &lt;code&gt;pkg/action/executor.go&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Dry-Run &amp; WhatIf</title><link>https://oakwood-commons.github.io/scafctl/docs/design/dryrun-whatif/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/dryrun-whatif/</guid><description>&lt;h1 id="dry-run--whatif-architecture"&gt;Dry-Run &amp;amp; WhatIf Architecture&lt;a class="anchor" href="#dry-run--whatif-architecture"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document describes the two complementary dry-run mechanisms in scafctl and how they interact.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;scafctl has two distinct dry-run paths:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Mechanism&lt;/th&gt;
 &lt;th&gt;Scope&lt;/th&gt;
 &lt;th&gt;Command&lt;/th&gt;
 &lt;th&gt;How it works&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;WhatIf&lt;/strong&gt; (solution-level)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;run solution --dry-run&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Solution dry-run&lt;/td&gt;
 &lt;td&gt;Resolvers execute normally (side-effect-free); actions are &lt;strong&gt;never invoked&lt;/strong&gt; — each action&amp;rsquo;s provider generates a WhatIf description&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;DryRunFromContext&lt;/strong&gt; (provider-level)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;run provider --dry-run&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Provider dry-run&lt;/td&gt;
 &lt;td&gt;The provider&amp;rsquo;s &lt;code&gt;Execute()&lt;/code&gt; method is called with &lt;code&gt;DryRunFromContext(ctx) == true&lt;/code&gt;; providers return mock data&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="why-two-mechanisms"&gt;Why Two Mechanisms?&lt;a class="anchor" href="#why-two-mechanisms"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Solution-level dry-run&lt;/strong&gt; answers &amp;ldquo;what would this solution do?&amp;rdquo; — it needs real resolver values to materialize action inputs and generate accurate WhatIf messages. Since resolvers are side-effect-free by design, they run normally.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provider-level dry-run&lt;/strong&gt; answers &amp;ldquo;what would this provider return?&amp;rdquo; — useful for testing a single provider in isolation. The provider itself decides what mock data to return.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="solution-level-dry-run-whatif"&gt;Solution-Level Dry-Run (WhatIf)&lt;a class="anchor" href="#solution-level-dry-run-whatif"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="flow"&gt;Flow&lt;a class="anchor" href="#flow"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;┌──────────────────┐ ┌────────────────────┐ ┌─────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ Execute resolvers │────&amp;gt;│ dryrun.Generate() │────&amp;gt;│ WhatIf Report │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ (real data) │ │ │ │ (JSON/YAML/ │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ │ For each action: │ │ table) │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ │ - Materialize │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ │ inputs │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ │ - Call provider&amp;#39;s │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ │ │ DescribeWhatIf() │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└──────────────────┘ └────────────────────┘ └─────────────────┘&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;Resolvers execute normally — they are side-effect-free, so real data is available&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dryrun.Generate()&lt;/code&gt; builds the action graph and materializes inputs using real resolver data&lt;/li&gt;
&lt;li&gt;For each action, &lt;code&gt;Descriptor.DescribeWhatIf(ctx, inputs)&lt;/code&gt; is called to get a provider-specific message&lt;/li&gt;
&lt;li&gt;Actions are &lt;strong&gt;never&lt;/strong&gt; executed — only their WhatIf descriptions are used&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="whatif-on-descriptor"&gt;WhatIf on Descriptor&lt;a class="anchor" href="#whatif-on-descriptor"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Providers implement &lt;code&gt;WhatIf&lt;/code&gt; on their &lt;code&gt;Descriptor&lt;/code&gt; to generate context-specific descriptions:&lt;/p&gt;</description></item><item><title>Solution Provider</title><link>https://oakwood-commons.github.io/scafctl/docs/design/solution-provider/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/solution-provider/</guid><description>&lt;h1 id="solution-provider"&gt;Solution Provider&lt;a class="anchor" href="#solution-provider"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; February 9, 2026&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="design-philosophy"&gt;Design Philosophy&lt;a class="anchor" href="#design-philosophy"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Three guiding principles:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fail loud by default, opt into silence.&lt;/strong&gt; Sub-solution failures should be Go errors unless the user explicitly asks for envelope-only reporting. This prevents the most common mistake (forgetting to check &lt;code&gt;status&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reuse existing types everywhere.&lt;/strong&gt; The codebase already has &lt;code&gt;ValueRef&lt;/code&gt;, &lt;code&gt;get.Interface&lt;/code&gt;, &lt;code&gt;resolver.Context&lt;/code&gt;, &lt;code&gt;action.ExecutionResult&lt;/code&gt;, etc. The solution provider should compose these, not reinvent them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Separation of loading vs execution.&lt;/strong&gt; The provider should do one thing — execute a loaded solution. Loading is delegated entirely to &lt;code&gt;get.Interface&lt;/code&gt;. This makes testing trivial and keeps the provider focused.&lt;/p&gt;</description></item><item><title>State</title><link>https://oakwood-commons.github.io/scafctl/docs/design/state/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/state/</guid><description>&lt;h1 id="state"&gt;State&lt;a class="anchor" href="#state"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;a class="anchor" href="#purpose"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;State adds optional, per-solution persistence of resolver values across executions. It enables two primary workflows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Re-run with same data&lt;/strong&gt; — Execute a solution repeatedly and retain resolved values between runs without re-prompting or re-fetching.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validation replay&lt;/strong&gt; — A validation application can replay the exact command with the same flags and verify it produces the same results.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;State is opt-in. Solutions without a &lt;code&gt;state&lt;/code&gt; block behave exactly as they do today — stateless, deterministic, and self-contained. State does not change the resolver or provider execution model. It adds a persistence layer accessed exclusively through the provider system.&lt;/p&gt;</description></item><item><title>Catalog Build Bundling</title><link>https://oakwood-commons.github.io/scafctl/docs/design/catalog-build-bundling/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/catalog-build-bundling/</guid><description>&lt;h1 id="solution-file-bundling"&gt;Solution File Bundling&lt;a class="anchor" href="#solution-file-bundling"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; February 9, 2026&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="problem-statement"&gt;Problem Statement&lt;a class="anchor" href="#problem-statement"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a solution is built (&lt;code&gt;scafctl build solution&lt;/code&gt;) and pushed to a catalog, only the solution YAML file is stored as a single OCI layer. Three categories of dependencies are lost:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Local file references&lt;/strong&gt; — template files read by the &lt;code&gt;file&lt;/code&gt; provider, sub-solutions used by the &lt;code&gt;solution&lt;/code&gt; provider, or other local resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-file solution parts&lt;/strong&gt; — solutions split across multiple YAML files (e.g., &lt;code&gt;resolvers.yaml&lt;/code&gt;, &lt;code&gt;workflow.yaml&lt;/code&gt;) that compose the complete solution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote catalog dependencies&lt;/strong&gt; — sub-solutions referenced by catalog name (e.g., &lt;code&gt;deploy-to-k8s@2.0.0&lt;/code&gt;) that must be fetched from a registry at runtime.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means solutions are not portable across machines, teams, or environments.&lt;/p&gt;</description></item><item><title>Output Directory</title><link>https://oakwood-commons.github.io/scafctl/docs/design/output-dir/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/output-dir/</guid><description>&lt;h1 id="output-directory"&gt;Output Directory&lt;a class="anchor" href="#output-directory"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;--output-dir&lt;/code&gt; flag scopes the action/workflow phase to a target directory, creating a clear phase-based separation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resolvers&lt;/strong&gt; always operate in CWD (current working directory) — they gather data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Actions&lt;/strong&gt; operate in the output directory when &lt;code&gt;--output-dir&lt;/code&gt; is set — they produce output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When &lt;code&gt;--output-dir&lt;/code&gt; is not set, actions continue using CWD (backward compatible).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mental-model"&gt;Mental Model&lt;a class="anchor" href="#mental-model"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CWD (current working directory)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── solution.yaml ← parsed by scafctl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── templates/ ← read by resolvers
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── app.yaml.tpl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── data/ ← read by resolvers
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; └── config.json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;--output-dir /path/to/output
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── app.yaml ← written by actions
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── config/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── settings.json ← written by actions
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── scripts/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; └── deploy.sh ← written by actions&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="phase-semantics"&gt;Phase Semantics&lt;a class="anchor" href="#phase-semantics"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Phase&lt;/th&gt;
 &lt;th&gt;Directory&lt;/th&gt;
 &lt;th&gt;Behavior&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Resolver&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;CWD (always)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;file&lt;/code&gt; provider reads from CWD, &lt;code&gt;directory&lt;/code&gt; lists CWD, etc.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Action&lt;/strong&gt; (with &lt;code&gt;--output-dir&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;Output directory&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;file&lt;/code&gt; write goes to output-dir, &lt;code&gt;directory&lt;/code&gt; mkdir goes to output-dir&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Action&lt;/strong&gt; (without flag)&lt;/td&gt;
 &lt;td&gt;CWD&lt;/td&gt;
 &lt;td&gt;Same as today — full backward compatibility&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="usage"&gt;Usage&lt;a class="anchor" href="#usage"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="cli-flag"&gt;CLI Flag&lt;a class="anchor" href="#cli-flag"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Actions write to /tmp/output instead of CWD&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl run solution -f solution.yaml --output-dir /tmp/output
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Dry-run with output-dir shows target paths&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl run solution -f solution.yaml --output-dir /tmp/output --dry-run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Run resolvers only (--output-dir accepted but has no effect)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl run resolver -f solution.yaml --output-dir /tmp/output -o json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Run a specific provider in action mode with output-dir&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl run provider file --capability action --output-dir /tmp/output &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -i &lt;span style="color:#a5d6ff"&gt;&amp;#39;{&amp;#34;operation&amp;#34;: &amp;#34;write&amp;#34;, &amp;#34;path&amp;#34;: &amp;#34;hello.txt&amp;#34;, &amp;#34;content&amp;#34;: &amp;#34;world&amp;#34;}&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="default-setting"&gt;Default Setting&lt;a class="anchor" href="#default-setting"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A default output directory can be configured in the app config so you don&amp;rsquo;t need to pass the flag every time:&lt;/p&gt;</description></item><item><title>Catalog CLI Design Decision</title><link>https://oakwood-commons.github.io/scafctl/docs/design/catalog-cli-design-decision/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/catalog-cli-design-decision/</guid><description>&lt;h1 id="catalog-cli-design-decision-pushpull-reference-patterns"&gt;Catalog CLI Design Decision: Push/Pull Reference Patterns&lt;a class="anchor" href="#catalog-cli-design-decision-pushpull-reference-patterns"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Decided — Option B&lt;br&gt;
&lt;strong&gt;Created:&lt;/strong&gt; 2026-02-06&lt;br&gt;
&lt;strong&gt;Decided:&lt;/strong&gt; 2026-02-07&lt;br&gt;
&lt;strong&gt;Decision:&lt;/strong&gt; Keep current &lt;code&gt;--catalog&lt;/code&gt; flag approach with config-based default catalog resolution.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="context"&gt;Context&lt;a class="anchor" href="#context"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Currently, &lt;code&gt;scafctl catalog push&lt;/code&gt; requires a &lt;code&gt;--catalog&lt;/code&gt; flag to specify the target registry:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl catalog push my-solution@1.0.0 --catalog ghcr.io/myorg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Pushes to: ghcr.io/myorg/solutions/my-solution:1.0.0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This differs from docker/podman, which uses full image names:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker tag my-image ghcr.io/myorg/my-image:1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker push ghcr.io/myorg/my-image:1.0.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We need to decide if we should align with the docker pattern, keep the current approach, or support both.&lt;/p&gt;</description></item><item><title>Working Directory (--cwd)</title><link>https://oakwood-commons.github.io/scafctl/docs/design/cwd/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/cwd/</guid><description>&lt;h1 id="working-directory---cwd"&gt;Working Directory (&lt;code&gt;--cwd&lt;/code&gt;)&lt;a class="anchor" href="#working-directory---cwd"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;--cwd&lt;/code&gt; (short: &lt;code&gt;-C&lt;/code&gt;) global flag changes the logical working directory for all path resolution without mutating the process CWD. It behaves similarly to &lt;code&gt;git -C&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl --cwd /path/to/project run solution -f solution.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scafctl -C /path/to/project run resolver -f demo.yaml -o json&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When &lt;code&gt;--cwd&lt;/code&gt; is set, &lt;strong&gt;all&lt;/strong&gt; relative paths—including &lt;code&gt;-f&lt;/code&gt;, &lt;code&gt;--output-dir&lt;/code&gt;, snapshot paths, and auto-discovery—resolve against the specified directory instead of the process CWD.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="architecture"&gt;Architecture&lt;a class="anchor" href="#architecture"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="context-based-resolution"&gt;Context-Based Resolution&lt;a class="anchor" href="#context-based-resolution"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--cwd&lt;/code&gt; flag is injected into the Go &lt;code&gt;context.Context&lt;/code&gt; via &lt;code&gt;provider.WithWorkingDirectory()&lt;/code&gt;, avoiding global state mutation. This design:&lt;/p&gt;</description></item><item><title>JSON Schema Migration Decision</title><link>https://oakwood-commons.github.io/scafctl/docs/design/jsonschema-migration-decision/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/jsonschema-migration-decision/</guid><description>&lt;h1 id="provider-schema-migration-custom-schemadefinition--json-schema"&gt;Provider Schema Migration: Custom SchemaDefinition → JSON Schema&lt;a class="anchor" href="#provider-schema-migration-custom-schemadefinition--json-schema"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Implemented&lt;br&gt;
&lt;strong&gt;Created:&lt;/strong&gt; 2026-02-07&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; (team discussion)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="context"&gt;Context&lt;a class="anchor" href="#context"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Provider input and output schemas are currently defined using custom types — &lt;code&gt;SchemaDefinition&lt;/code&gt; and &lt;code&gt;PropertyDefinition&lt;/code&gt; — in &lt;code&gt;pkg/provider/provider.go&lt;/code&gt;. These types were purpose-built early in development and serve their role for simple, flat property definitions.&lt;/p&gt;
&lt;p&gt;Meanwhile, the codebase already uses &lt;a href="https://github.com/google/jsonschema-go" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;github.com/google/jsonschema-go&lt;/code&gt;&lt;/a&gt;
 (v0.4.2) for &lt;strong&gt;action result validation&lt;/strong&gt; (&lt;code&gt;Action.ResultSchema&lt;/code&gt;), where it provides full JSON Schema 2020-12 support including &lt;code&gt;$ref&lt;/code&gt;, &lt;code&gt;allOf&lt;/code&gt;/&lt;code&gt;anyOf&lt;/code&gt;/&lt;code&gt;oneOf&lt;/code&gt;, nested objects, and more.&lt;/p&gt;</description></item><item><title>File Conflict Strategies</title><link>https://oakwood-commons.github.io/scafctl/docs/design/file-conflict-strategies/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/file-conflict-strategies/</guid><description>&lt;h1 id="file-conflict-strategies"&gt;File Conflict Strategies&lt;a class="anchor" href="#file-conflict-strategies"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The file provider currently writes files unconditionally — &lt;code&gt;os.WriteFile()&lt;/code&gt; silently overwrites any existing content. This creates problems for scaffolding workflows where solutions are run repeatedly during development: generated files may overwrite user edits, there is no feedback about what changed, and no way to control write behavior per-file.&lt;/p&gt;
&lt;p&gt;This design introduces a &lt;strong&gt;conflict strategy system&lt;/strong&gt; (&lt;code&gt;onConflict&lt;/code&gt;) to the file provider&amp;rsquo;s &lt;code&gt;write&lt;/code&gt; and &lt;code&gt;write-tree&lt;/code&gt; operations. It adds content-based change detection (SHA256 checksums), optional file backups, an append mode with line-level deduplication, and detailed per-file status reporting.&lt;/p&gt;</description></item><item><title>Hugo Guide</title><link>https://oakwood-commons.github.io/scafctl/docs/design/hugo-guide/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/hugo-guide/</guid><description>&lt;h1 id="hugo-guide-for-scafctl"&gt;Hugo Guide for scafctl&lt;a class="anchor" href="#hugo-guide-for-scafctl"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This guide explains how to set up and use Hugo for the scafctl documentation site.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;a class="anchor" href="#prerequisites"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Go 1.21+ installed (Hugo extended is recommended)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installation"&gt;Installation&lt;a class="anchor" href="#installation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="option-1-using-homebrew-macos"&gt;Option 1: Using Homebrew (macOS)&lt;a class="anchor" href="#option-1-using-homebrew-macos"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install hugo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="option-2-using-go"&gt;Option 2: Using Go&lt;a class="anchor" href="#option-2-using-go"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go install github.com/gohugoio/hugo@latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="option-3-download-binary"&gt;Option 3: Download Binary&lt;a class="anchor" href="#option-3-download-binary"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Download from &lt;a href="https://github.com/gohugoio/hugo/releases" target="_blank" rel="noopener noreferrer"&gt;Hugo Releases&lt;/a&gt;
 and add to your PATH.&lt;/p&gt;
&lt;h3 id="verify-installation"&gt;Verify Installation&lt;a class="anchor" href="#verify-installation"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hugo version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="initial-setup-one-time"&gt;Initial Setup (One-time)&lt;a class="anchor" href="#initial-setup-one-time"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After cloning the repository, initialize Hugo modules and download the theme:&lt;/p&gt;</description></item><item><title>Identity Provider</title><link>https://oakwood-commons.github.io/scafctl/docs/design/identity-provider/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/identity-provider/</guid><description>&lt;h1 id="identity-provider"&gt;Identity Provider&lt;a class="anchor" href="#identity-provider"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The &lt;strong&gt;identity&lt;/strong&gt; provider exposes authentication identity information from auth handlers. It returns non-sensitive identity data such as claims, authentication status, and identity type. It &lt;strong&gt;never exposes tokens or other secrets&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="operations"&gt;Operations&lt;a class="anchor" href="#operations"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Operation&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Scope Support&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;claims&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Returns identity claims (name, email, subject, etc.)&lt;/td&gt;
 &lt;td&gt;Yes — parse scoped access token JWT&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;status&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Returns authentication status, expiry, identity type&lt;/td&gt;
 &lt;td&gt;Yes — return scoped token metadata&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;groups&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Returns Entra group memberships (ObjectIDs)&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;list&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Lists all registered auth handlers&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="inputs"&gt;Inputs&lt;a class="anchor" href="#inputs"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Required&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;operation&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Operation to perform: &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;claims&lt;/code&gt;, &lt;code&gt;groups&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;handler&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;Auth handler name (e.g., &lt;code&gt;entra&lt;/code&gt;). Defaults to the first authenticated handler.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;scope&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;OAuth scope for scoped token operations. When set, &lt;code&gt;claims&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt; operations mint a token with this scope and return its details instead of stored metadata.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="default-behavior-without-scope"&gt;Default Behavior (without scope)&lt;a class="anchor" href="#default-behavior-without-scope"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When no &lt;code&gt;scope&lt;/code&gt; is provided, the identity provider reads &lt;strong&gt;stored metadata&lt;/strong&gt; from the auth handler&amp;rsquo;s local session. This metadata was extracted from the OIDC ID token JWT at login time:&lt;/p&gt;</description></item><item><title>Go Template LRU Cache</title><link>https://oakwood-commons.github.io/scafctl/docs/design/go-template-cache/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/go-template-cache/</guid><description>&lt;h1 id="go-template-lru-cache"&gt;Go Template LRU Cache&lt;a class="anchor" href="#go-template-lru-cache"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;pkg/gotmpl&lt;/code&gt; package includes a thread-safe LRU (Least Recently Used) cache for compiled Go templates. The cache avoids redundant parsing of identical template content across evaluations, significantly improving performance for solutions that evaluate the same templates repeatedly (e.g., during resolver phases with shared template logic).&lt;/p&gt;
&lt;h2 id="motivation"&gt;Motivation&lt;a class="anchor" href="#motivation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Go&amp;rsquo;s &lt;code&gt;text/template.Parse()&lt;/code&gt; is the dominant cost in template evaluation. In large solutions with many resolvers or actions that use similar templates, the same template content is re-parsed on every invocation. The LRU cache stores compiled &lt;code&gt;*template.Template&lt;/code&gt; objects keyed by a SHA-256 hash of their content and configuration, eliminating redundant parse operations while bounding memory usage.&lt;/p&gt;</description></item><item><title>Implementation Gaps</title><link>https://oakwood-commons.github.io/scafctl/docs/design/implementation-gaps/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/implementation-gaps/</guid><description>&lt;h1 id="implementation-gaps"&gt;Implementation Gaps&lt;a class="anchor" href="#implementation-gaps"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote class='book-hint '&gt;
&lt;p&gt;&lt;strong&gt;Generated:&lt;/strong&gt; 2026-02-25&lt;/p&gt;
&lt;p&gt;This document tracks features described in design documents that are &lt;strong&gt;not yet fully implemented&lt;/strong&gt;. Fully implemented features are omitted. For complete design specs, see the linked source documents.&lt;/p&gt;
&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2 id="deferred-explicitly-planned-for-future"&gt;Deferred (Explicitly Planned for Future)&lt;a class="anchor" href="#deferred-explicitly-planned-for-future"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="conditional-retry-retryif"&gt;Conditional Retry (&lt;code&gt;retryIf&lt;/code&gt;)&lt;a class="anchor" href="#conditional-retry-retryif"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://oakwood-commons.github.io/scafctl/docs/design/actions/"&gt;actions.md&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Retry policies with a &lt;code&gt;retryIf&lt;/code&gt; condition to retry only on specific error types. Requires an &lt;code&gt;__error&lt;/code&gt; namespace with &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;statusCode&lt;/code&gt;, &lt;code&gt;code&lt;/code&gt;, &lt;code&gt;retryable&lt;/code&gt;, and &lt;code&gt;attempt&lt;/code&gt; fields.&lt;/p&gt;
&lt;h3 id="matrix-strategy"&gt;Matrix Strategy&lt;a class="anchor" href="#matrix-strategy"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://oakwood-commons.github.io/scafctl/docs/design/actions/"&gt;actions.md&lt;/a&gt;
&lt;/p&gt;</description></item><item><title>MCP Server for scafctl Solutions</title><link>https://oakwood-commons.github.io/scafctl/docs/design/mcp-server/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/mcp-server/</guid><description>&lt;h1 id="mcp-server-for-scafctl-solutions"&gt;MCP Server for scafctl Solutions&lt;a class="anchor" href="#mcp-server-for-scafctl-solutions"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This document evaluates building a &lt;a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer"&gt;Model Context Protocol (MCP)&lt;/a&gt;
 server for scafctl solutions. An MCP server exposes &lt;strong&gt;tools&lt;/strong&gt;, &lt;strong&gt;resources&lt;/strong&gt;, and &lt;strong&gt;prompts&lt;/strong&gt; over JSON-RPC 2.0 (typically via stdio or SSE) so that AI agents (Claude, Copilot, etc.) can discover and invoke them programmatically.&lt;/p&gt;
&lt;h2 id="how-an-mcp-server-works-internally"&gt;How an MCP Server Works Internally&lt;a class="anchor" href="#how-an-mcp-server-works-internally"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="the-big-picture"&gt;The Big Picture&lt;a class="anchor" href="#the-big-picture"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An MCP server is a &lt;strong&gt;long-running process&lt;/strong&gt; that sits between an AI agent (e.g., Claude, Copilot) and your application. It speaks a specific protocol (JSON-RPC 2.0) so the AI knows what capabilities are available and how to call them.&lt;/p&gt;</description></item><item><title>MCP Server Implementation Guide</title><link>https://oakwood-commons.github.io/scafctl/docs/design/mcp-server-implementation-guide/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/mcp-server-implementation-guide/</guid><description>&lt;h1 id="mcp-server-implementation-guide"&gt;MCP Server Implementation Guide&lt;a class="anchor" href="#mcp-server-implementation-guide"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document is the detailed, step-by-step implementation guide for building the MCP server into scafctl. It is the companion to the &lt;a href="https://oakwood-commons.github.io/scafctl/docs/design/mcp-server/"&gt;MCP Server design document&lt;/a&gt;
 which covers the &lt;em&gt;why&lt;/em&gt; — this document covers the &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;a class="anchor" href="#prerequisites"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before starting implementation, the following must be true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All preparatory refactoring is &lt;strong&gt;complete&lt;/strong&gt; (see &lt;a href="https://oakwood-commons.github.io/scafctl/docs/design/mcp-server/#completed-preparatory-refactoring"&gt;mcp-server.md § Completed Preparatory Refactoring&lt;/a&gt;
)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkg/mcp/context.go&lt;/code&gt; exists with &lt;code&gt;NewContext()&lt;/code&gt; and functional options&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkg/cmd/scafctl/run/execute.go&lt;/code&gt; exports &lt;code&gt;ValidateSolution()&lt;/code&gt;, &lt;code&gt;ExecuteResolvers()&lt;/code&gt;, &lt;code&gt;ResolverExecutionConfigFromContext()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkg/solution/prepare/prepare.go&lt;/code&gt; exports &lt;code&gt;Solution()&lt;/code&gt; with functional options&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkg/cmd/scafctl/explain/results.go&lt;/code&gt; exports &lt;code&gt;BuildSolutionExplanation()&lt;/code&gt;, &lt;code&gt;LoadSolution()&lt;/code&gt;, &lt;code&gt;LookupProvider()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkg/cmd/scafctl/get/celfunction/celfunction.go&lt;/code&gt; provides the &lt;code&gt;cel-functions&lt;/code&gt; command&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="decisions-summary"&gt;Decisions Summary&lt;a class="anchor" href="#decisions-summary"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These decisions were made during planning and are &lt;strong&gt;final&lt;/strong&gt; for this implementation:&lt;/p&gt;</description></item><item><title>MCP Server Enhancements &amp; CLI Parity</title><link>https://oakwood-commons.github.io/scafctl/docs/design/mcp-server-enhancements/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/mcp-server-enhancements/</guid><description>&lt;h1 id="mcp-server-enhancements--cli-parity"&gt;MCP Server Enhancements &amp;amp; CLI Parity&lt;a class="anchor" href="#mcp-server-enhancements--cli-parity"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document is the implementation plan for the next phase of MCP server improvements and the introduction of CLI parity for MCP-only tools. It builds on the existing &lt;a href="https://oakwood-commons.github.io/scafctl/docs/design/mcp-server/"&gt;MCP Server design&lt;/a&gt;
 and &lt;a href="https://oakwood-commons.github.io/scafctl/docs/design/mcp-server-implementation-guide/"&gt;Implementation Guide&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;a class="anchor" href="#table-of-contents"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#motivation"&gt;Motivation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#part-1-cli-parity--exposing-mcp-tools-as-cli-commands"&gt;Part 1: CLI Parity — Exposing MCP Tools as CLI Commands&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="#architecture-shared-library-pattern"&gt;Architecture: Shared Library Pattern&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-1a-scafctl-eval-command-group"&gt;Phase 1A: &lt;code&gt;scafctl eval&lt;/code&gt; Command Group&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-1b-scafctl-new-command"&gt;Phase 1B: &lt;code&gt;scafctl new&lt;/code&gt; Command&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-1c-scafctl-lint-subcommands"&gt;Phase 1C: &lt;code&gt;scafctl lint&lt;/code&gt; Subcommands&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-1e-scafctl-examples-command-group"&gt;Phase 1E: &lt;code&gt;scafctl examples&lt;/code&gt; Command Group&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-1f-enhanced---dry-run-output"&gt;Phase 1F: Enhanced &lt;code&gt;--dry-run&lt;/code&gt; Output&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#part-2-new-mcp-tools"&gt;Part 2: New MCP Tools&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="#phase-2a-extract_resolver_refs"&gt;Phase 2A: &lt;code&gt;extract_resolver_refs&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-2b-generate_test_scaffold"&gt;Phase 2B: &lt;code&gt;generate_test_scaffold&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-2c-list_tests"&gt;Phase 2C: &lt;code&gt;list_tests&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-2d-show_snapshot"&gt;Phase 2D: &lt;code&gt;show_snapshot&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-2e-diff_snapshots"&gt;Phase 2E: &lt;code&gt;diff_snapshots&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-2f-catalog_inspect"&gt;Phase 2F: &lt;code&gt;catalog_inspect&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-2g-list_auth_handlers"&gt;Phase 2G: &lt;code&gt;list_auth_handlers&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-2h-get_config_paths"&gt;Phase 2H: &lt;code&gt;get_config_paths&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-2i-validate_expressions-batch"&gt;Phase 2I: &lt;code&gt;validate_expressions&lt;/code&gt; (Batch)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#part-3-new-mcp-prompts"&gt;Part 3: New MCP Prompts&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="#phase-3a-analyze_execution-prompt"&gt;Phase 3A: &lt;code&gt;analyze_execution&lt;/code&gt; Prompt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-3b-migrate_solution-prompt"&gt;Phase 3B: &lt;code&gt;migrate_solution&lt;/code&gt; Prompt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-3c-optimize_solution-prompt"&gt;Phase 3C: &lt;code&gt;optimize_solution&lt;/code&gt; Prompt&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#part-4-new-mcp-resources"&gt;Part 4: New MCP Resources&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="#phase-4a-solutionnametests-resource"&gt;Phase 4A: &lt;code&gt;solution://{name}/tests&lt;/code&gt; Resource&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#part-5-architecture--quality-improvements"&gt;Part 5: Architecture &amp;amp; Quality Improvements&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="#phase-5a-extract-shared-libraries-from-inline-mcp-code"&gt;Phase 5A: Extract Shared Libraries from Inline MCP Code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-5b-structured-error-context"&gt;Phase 5B: Structured Error Context&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-5c-tool-latency-hints"&gt;Phase 5C: Tool Latency Hints&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#phase-5d-get_version-tool"&gt;Phase 5D: &lt;code&gt;get_version&lt;/code&gt; Tool&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#implementation-order"&gt;Implementation Order&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#testing-strategy"&gt;Testing Strategy&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="motivation"&gt;Motivation&lt;a class="anchor" href="#motivation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The MCP server currently exposes 26 tools, 9 prompts, and 5 resources. After analyzing the full codebase, two categories of improvements have been identified:&lt;/p&gt;</description></item><item><title/><link>https://oakwood-commons.github.io/scafctl/docs/design/api-surface/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/api-surface/</guid><description>&lt;h1 id="api-surface-design"&gt;API Surface Design&lt;a class="anchor" href="#api-surface-design"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Detailed API surface for the scafctl REST API server. All endpoints use JSON request/response bodies and follow REST conventions.&lt;/p&gt;
&lt;h2 id="base-url"&gt;Base URL&lt;a class="anchor" href="#base-url"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;http://localhost:8080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Version-prefixed endpoints use &lt;code&gt;/{apiVersion}/&lt;/code&gt; (default: &lt;code&gt;/v1/&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="authentication"&gt;Authentication&lt;a class="anchor" href="#authentication"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Method&lt;/strong&gt;: Entra OIDC (Azure AD) JWT Bearer tokens&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Header&lt;/strong&gt;: &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Public endpoints&lt;/strong&gt;: Health probes and metrics bypass authentication&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Admin endpoints&lt;/strong&gt;: Require authentication + &lt;code&gt;admin&lt;/code&gt; role claim (or localhost-only when auth is disabled)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="response-format"&gt;Response Format&lt;a class="anchor" href="#response-format"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="success-single-resource"&gt;Success (single resource)&lt;a class="anchor" href="#success-single-resource"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;fieldA&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;fieldB&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="success-collection-with-pagination"&gt;Success (collection with pagination)&lt;a class="anchor" href="#success-collection-with-pagination"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;items&amp;#34;&lt;/span&gt;: [&lt;span style="color:#f85149"&gt;...&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;pagination&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;page&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;per_page&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;100&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;total_items&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;250&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;total_pages&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;3&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;has_more&amp;#34;&lt;/span&gt;: &lt;span style="color:#79c0ff"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="error-rfc-7807-problem-details"&gt;Error (RFC 7807 Problem Details)&lt;a class="anchor" href="#error-rfc-7807-problem-details"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;&amp;#34;Bad Request&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;400&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;&amp;#34;validation failed: name is required&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="naming-conventions"&gt;Naming Conventions&lt;a class="anchor" href="#naming-conventions"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;Convention&lt;/th&gt;
 &lt;th&gt;Example&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Config (YAML/JSON)&lt;/td&gt;
 &lt;td&gt;camelCase&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;apiVersion&lt;/code&gt;, &lt;code&gt;shutdownTimeout&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;API response fields&lt;/td&gt;
 &lt;td&gt;snake_case&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;per_page&lt;/code&gt;, &lt;code&gt;total_items&lt;/code&gt;, &lt;code&gt;has_more&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;URL paths&lt;/td&gt;
 &lt;td&gt;lowercase, kebab-case for multi-word&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/solutions&lt;/code&gt;, &lt;code&gt;/v1/admin/reload-config&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Query parameters&lt;/td&gt;
 &lt;td&gt;snake_case&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;per_page&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="endpoints"&gt;Endpoints&lt;a class="anchor" href="#endpoints"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="operational-root-router--no-auth"&gt;Operational (Root Router — No Auth)&lt;a class="anchor" href="#operational-root-router--no-auth"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;API root with HATEOAS links&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/health&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Full health check with component status&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/health/live&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Liveness probe&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/health/ready&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Readiness probe&lt;/td&gt;
 &lt;td&gt;200, 503&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/metrics&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Prometheus metrics&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="solutions"&gt;Solutions&lt;a class="anchor" href="#solutions"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Query Params&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/solutions/run&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Run a solution (resolve + execute actions)&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 400, 422&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/solutions/render&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Resolve inputs without executing actions&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 400, 422&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/solutions/lint&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Lint a solution&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/solutions/inspect&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Inspect solution structure&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/solutions/test&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Validate solution against provider registry&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/solutions/dryrun&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Dry-run a solution (what-if analysis)&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 400, 422&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="providers"&gt;Providers&lt;a class="anchor" href="#providers"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Query Params&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/providers&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;List providers&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;page&lt;/code&gt;, &lt;code&gt;per_page&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/providers/{name}&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get provider details&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 404&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/providers/{name}/schema&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get provider schema&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 404&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="eval"&gt;Eval&lt;a class="anchor" href="#eval"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/eval/cel&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Evaluate CEL expression&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/eval/template&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Evaluate Go template&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="catalogs"&gt;Catalogs&lt;a class="anchor" href="#catalogs"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Query Params&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/catalogs&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;List catalogs&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;page&lt;/code&gt;, &lt;code&gt;per_page&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/catalogs/{name}&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get catalog details&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 404&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/catalogs/{name}/solutions&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;List catalog solutions&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;page&lt;/code&gt;, &lt;code&gt;per_page&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;200, 404&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/catalogs/sync&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Sync catalogs (planned)&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="schemas"&gt;Schemas&lt;a class="anchor" href="#schemas"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Query Params&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/schemas&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;List schemas&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;page&lt;/code&gt;, &lt;code&gt;per_page&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/schemas/{name}&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get schema&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 404&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/schemas/validate&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Validate against schema&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 400, 422&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="config"&gt;Config&lt;a class="anchor" href="#config"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/config&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get current config&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/settings&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get runtime settings&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="snapshots"&gt;Snapshots&lt;a class="anchor" href="#snapshots"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Query Params&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/snapshots&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;List snapshots (planned)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;page&lt;/code&gt;, &lt;code&gt;per_page&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/snapshots/{id}&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get snapshot details (planned)&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;200, 404&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="explain--diff"&gt;Explain &amp;amp; Diff&lt;a class="anchor" href="#explain--diff"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/explain&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Explain a solution&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/diff&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Diff solutions&lt;/td&gt;
 &lt;td&gt;200, 400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="admin"&gt;Admin&lt;a class="anchor" href="#admin"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Authorization&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/admin/info&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Server info&lt;/td&gt;
 &lt;td&gt;admin role / localhost&lt;/td&gt;
 &lt;td&gt;200, 403&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/admin/reload-config&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Hot-reload config (planned)&lt;/td&gt;
 &lt;td&gt;admin role / localhost&lt;/td&gt;
 &lt;td&gt;200, 403&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/admin/clear-cache&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Clear caches (planned)&lt;/td&gt;
 &lt;td&gt;admin role / localhost&lt;/td&gt;
 &lt;td&gt;200, 403&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="documentation-auto-generated-by-huma"&gt;Documentation (Auto-Generated by Huma)&lt;a class="anchor" href="#documentation-auto-generated-by-huma"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Status Codes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/docs&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Interactive API documentation&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/v1/openapi.json&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;OpenAPI specification (JSON)&lt;/td&gt;
 &lt;td&gt;200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="common-query-parameters"&gt;Common Query Parameters&lt;a class="anchor" href="#common-query-parameters"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Parameter&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Default&lt;/th&gt;
 &lt;th&gt;Constraints&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;page&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;int&lt;/td&gt;
 &lt;td&gt;Page number (1-indexed)&lt;/td&gt;
 &lt;td&gt;1&lt;/td&gt;
 &lt;td&gt;1–10000&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;per_page&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;int&lt;/td&gt;
 &lt;td&gt;Items per page&lt;/td&gt;
 &lt;td&gt;100&lt;/td&gt;
 &lt;td&gt;1–1000&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;filter&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;CEL filter expression&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;max 2000 chars&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="global-status-codes"&gt;Global Status Codes&lt;a class="anchor" href="#global-status-codes"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These status codes can be returned by any endpoint, in addition to endpoint-specific codes:&lt;/p&gt;</description></item><item><title/><link>https://oakwood-commons.github.io/scafctl/docs/design/web-api-implementation-plan/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/web-api-implementation-plan/</guid><description>&lt;h1 id="web-api-implementation-plan"&gt;Web API Implementation Plan&lt;a class="anchor" href="#web-api-implementation-plan"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Add a &lt;a href="https://github.com/danielgtaylor/huma" target="_blank" rel="noopener noreferrer"&gt;Huma&lt;/a&gt;
+&lt;a href="https://github.com/go-chi/chi" target="_blank" rel="noopener noreferrer"&gt;chi&lt;/a&gt;
-based REST API to scafctl, started via a new &lt;code&gt;scafctl serve&lt;/code&gt; command. The API mirrors all major CLI features (run, render, lint, eval, catalog, solutions, providers, config, etc.) with Entra OIDC authentication, Prometheus metrics, OpenTelemetry tracing, audit logging, CEL-based filtering, and admin endpoints.&lt;/p&gt;
&lt;p&gt;Architecture follows the &lt;a href="https://github.com/ford-cloud/jqapi" target="_blank" rel="noopener noreferrer"&gt;jqapi&lt;/a&gt;
 reference implementation: chi router → layered middleware → Huma endpoint registration → handler context with dependency injection. All operations are synchronous request-response initially, with an async task model planned as future work.&lt;/p&gt;</description></item><item><title>Entra ID Authentication Implementation Decision</title><link>https://oakwood-commons.github.io/scafctl/docs/design/entra-auth-implementation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://oakwood-commons.github.io/scafctl/docs/design/entra-auth-implementation/</guid><description>&lt;h1 id="entra-id-authentication-manual-implementation-vs-msal"&gt;Entra ID Authentication: Manual Implementation vs MSAL&lt;a class="anchor" href="#entra-id-authentication-manual-implementation-vs-msal"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="status"&gt;Status&lt;a class="anchor" href="#status"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Decided&lt;/strong&gt; — Manual implementation retained.&lt;/p&gt;
&lt;h2 id="context"&gt;Context&lt;a class="anchor" href="#context"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;pkg/auth/entra&lt;/code&gt; package implements OAuth 2.0 authentication against Azure Entra ID (formerly Azure AD). A question arose whether to use the official &lt;a href="https://github.com/AzureAD/microsoft-authentication-library-for-go" target="_blank" rel="noopener noreferrer"&gt;Microsoft Authentication Library for Go (MSAL)&lt;/a&gt;
 instead of the current hand-rolled implementation.&lt;/p&gt;
&lt;h2 id="current-implementation"&gt;Current Implementation&lt;a class="anchor" href="#current-implementation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The package manually implements all OAuth 2.0 flows using raw &lt;code&gt;net/http&lt;/code&gt; POST calls to Azure Entra ID token endpoints. There are zero Azure SDK or MSAL dependencies in the project.&lt;/p&gt;</description></item></channel></rss>