From 6d3701ecda354242c0ca65bb622daac299dcc0df Mon Sep 17 00:00:00 2001 From: buu420 <43735748+buu420@users.noreply.github.com> Date: Tue, 4 Aug 2026 19:56:58 -0500 Subject: [PATCH 01/12] docs: design full AuthorTool CLI parity --- .../2026-08-04-full-author-cli-design.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-04-full-author-cli-design.md diff --git a/docs/superpowers/specs/2026-08-04-full-author-cli-design.md b/docs/superpowers/specs/2026-08-04-full-author-cli-design.md new file mode 100644 index 0000000..de07139 --- /dev/null +++ b/docs/superpowers/specs/2026-08-04-full-author-cli-design.md @@ -0,0 +1,199 @@ +# Full Accessibility Mod Manager Author CLI Design + +## Purpose + +Build a local, screen-reader-friendly command-line counterpart to RealAmethyst's current Accessibility Mod Manager AuthorTool. The CLI must provide feature parity with the AuthorTool while using the same models, validation rules, trust checks, packaging code, and publishing behavior. It is intended to let Codex and the user maintain Blind Soldier and other accessibility-mod catalogs without operating the WPF interface. + +Development starts from upstream commit `9e2d223762aa21a2fc765bae55380699a2532746`, where the AuthorTool is version 0.28.0. The older local `PluginIndexAuthor-0.25.1.exe` was inspected only to identify the user's existing workflow and is not an implementation base. + +This is a local personal-use modification. Nothing will be forked, pushed, submitted upstream, or redistributed until the user has spoken with RealAmethyst and obtained any permission that is required. No protected security or integrity mechanism will be weakened, bypassed, or replaced. + +## Chosen Approach + +Add a console application to the current Accessibility Mod Manager solution and move reusable non-UI author workflows into a shared authoring library. Both the existing WPF AuthorTool and the CLI will call those shared workflows. + +This is preferred over the alternatives: + +- Referencing the WPF executable directly would pull UI state and dialog callbacks into console operations and would be fragile as the GUI changes. +- Automating the AuthorTool window would retain the accessibility and focus problems that motivated the CLI. +- Independently reimplementing the JSON and publishing rules would allow the GUI and CLI to disagree and could bypass later safety fixes. + +## Project Structure + +Add these projects: + +- `AccessibilityModManager.Authoring`: a non-UI class library containing author workflow orchestration, typed command inputs and results, and reusable services currently embedded in the AuthorTool project or its view models. +- `AccessibilityModManager.AuthorCli`: a `net10.0-windows` console application published as `amm-author.exe`. + +Keep these existing projects: + +- `AccessibilityModManager.Core`: public models and interfaces. +- `AccessibilityModManager.Infrastructure`: installer, validation, security, network, and persistence implementation. +- `AccessibilityModManager.AuthorTool`: the WPF interface. It will consume the shared authoring library without losing existing behavior. + +The shared library will expose focused workflows rather than UI abstractions: + +- `ProjectWorkflow` +- `GameWorkflow` +- `DependencyWorkflow` +- `LifecycleScriptWorkflow` +- `PackageWorkflow` +- `ReleaseWorkflow` +- `IndexWorkflow` +- `PatreonWorkflow` +- `ServerWorkflow` +- `SigningWorkflow` +- `RegistryAdminWorkflow` + +Each workflow accepts typed input records and returns typed result records. Decisions that require consent return a preview describing the exact action. The WPF caller presents its existing dialog; the CLI caller presents a text prompt or requires `--yes` in noninteractive mode. + +## Command Surface + +Use `System.CommandLine` 2.0.10 for parsing, help, validation, and completion support. Commands are grouped by the AuthorTool feature they represent: + +```text +amm-author project ... +amm-author author ... +amm-author game ... +amm-author dependency ... +amm-author script ... +amm-author package ... +amm-author release ... +amm-author index ... +amm-author github ... +amm-author patreon ... +amm-author server ... +amm-author signing ... +amm-author registry ... +``` + +The command families cover: + +- Project creation, recent projects, opening local projects, listing writable GitHub repositories, cloning, pulling, and project status. +- Author profile display and editing. +- Game creation, editing, removal, and listing. +- Tags, languages, filters, executable and Steam detection fields, dependencies, dependency presets, check rules, and auto-install metadata. +- Pre-install, post-install, and post-uninstall lifecycle scripts, including external source paths and install-to-game-folder behavior. +- Wrapped package building, manifest generation, package validation, and SHA256 calculation. +- Release listing, creation, editing, removal, URL-only releases, GitHub uploads, server uploads, channels, notes, changelog links, and Patreon gates. +- Index loading, formatting, validation, reconciliation with the live catalog, publish-destination selection, saving, publishing, membership checks, and status. +- GitHub CLI availability, authentication, repository listing, release creation or update, asset replacement, index commit, and push. +- Patreon sign-in, sign-out, status, tier listing, post validation, attachment selection, and gated-release metadata. +- SFTP settings, host-key pinning, connection tests, public and gated asset upload, server self-test, and publish-lock inspection or removal. +- Catalog signing-key creation, import, export, backup, passphrase change, status, claim signing, head recovery, and reconciliation. +- Registry-admin project handling, JSON editing and validation, signing, registry-pair publication, release publication, and commit/push. + +Registry-admin commands are compiled only when the existing `RegistryAdmin` build property is enabled. A standard build will explain that the command is unavailable instead of silently omitting the reason. The CLI will not add an option that bypasses this build gate. + +## Project Resolution + +Commands that operate on a plugin catalog resolve the project in this order: + +1. The explicit `--project ` option. +2. The current directory when it contains `index.json`. +3. The last-opened project in `%LocalAppData%\AccessibilityModManager-Author\config.json`. + +The resolved absolute path and plugin id are printed before a mutating operation. The CLI shares the AuthorTool's existing configuration, recent-project list, source-repository mappings, script paths, server settings, DPAPI-protected credentials, signing keys, and publishing records. + +## Complete Release Flow + +Granular commands remain available, but `amm-author release publish` provides the AuthorTool's complete normal release workflow: + +1. Resolve and lock the project. +2. Load the author configuration and `index.json`. +3. Reconcile local and live catalog state using the existing trust rules. +4. Build or select a wrapped ZIP. +5. Copy the package into a private staging directory and hold it read-only. +6. Validate the manifest through `PluginPackageValidation` using the requested plugin id, game id, and version. +7. Hash the exact staged bytes. +8. Check the chosen GitHub, server, or Patreon destination. +9. Upload the same staged bytes that were validated and hashed. +10. Add or replace the release record in memory. +11. Validate the complete plugin index through `PluginIndexValidation`. +12. Save `index.json` durably with an updated `generatedAt` value. +13. Publish through the existing GitHub or signed-server coordinator. +14. Read back the live result and update the local publishing record. +15. Apply any deferred Patreon gate change only after the catalog describes it. + +If a later phase fails after an earlier remote phase succeeded, the CLI reports each completed and incomplete phase. It never converts partial completion into generic success. + +## Interaction and Accessibility + +Default output is concise plain text with no animated spinners, cursor rewriting, decorative tables, color-only distinctions, or unlabeled symbols. Progress is emitted as complete lines suitable for Prism, NVDA, Narrator, and terminal review. + +Human-readable status and warnings go to standard error. Requested data goes to standard output. `--json` writes one final structured result object to standard output while retaining progress on standard error. `--quiet` suppresses nonessential progress but never warnings or errors. + +Interactive mode asks one direct question at a time. Secret values use concealed console input. Automation can provide a secret through standard input using a purpose-specific `--passphrase-stdin` option. Passphrases, access tokens, private keys, and DPAPI plaintext are never accepted as ordinary command-line values and never written to logs. + +Every command has useful `--help` text and examples. Missing required input in noninteractive mode produces an immediate usage error instead of waiting for a prompt. + +## Confirmation and Dry Run + +Read-only commands never prompt. Ordinary local additions and edits may prompt for missing values but do not require redundant confirmation after all values have been displayed. + +Commands that delete metadata, replace an existing release asset, publish a catalog, change a Patreon gate, remove a signing key, break a publish lock, or alter the registry show one exact action summary and require confirmation. `--yes` supplies that confirmation for automation. It does not bypass validation, trust, authentication, or build gates. + +`--dry-run` performs parsing, project resolution, local/live reconciliation, package and catalog validation, and action planning without writing files, committing, uploading, signing, changing gates, or removing locks. + +## Security and Concurrency + +The CLI uses the same implementations as the current manager and AuthorTool for: + +- SHA256 package verification +- ZIP and path-safety checks +- Manifest action allowlisting +- HTTPS enforcement +- Plugin, game, and release identity binding +- Dependency uniqueness validation +- Registry signature and trust-anchor verification +- Catalog claim signing and replay protection +- Publisher-head tracking and reconciliation +- GitHub destination and repository-visibility checks +- SFTP host-key pinning +- Publish locks and compare-before-break behavior +- Patreon gate sequencing + +No `--force`, environment variable, debug mode, or admin mode may weaken these checks. + +A project-level cross-process lock protects `index.json` and author configuration from concurrent GUI and CLI writes. Existing remote publish locks continue to protect server operations. When another process owns a lock, the CLI identifies the lock and exits without changing state. + +## Exit Codes + +- `0`: completed successfully +- `2`: invalid command, missing input, or noninteractive prompt required +- `3`: package, manifest, index, or trust validation failed +- `4`: authentication, authorization, or configuration problem +- `5`: local or remote conflict, publishing failure, or partial completion +- `130`: cancelled by the user or Ctrl+C + +Each failure result contains a stable machine-readable error category in JSON mode in addition to the human explanation. + +## Testing + +Add tests at three levels: + +1. Parser and handler tests verify every command family, required options, project resolution, output routing, secret-input rules, confirmation behavior, JSON shape, and exit code. +2. Workflow tests use disposable projects to compare GUI-facing and CLI-facing operations against the same shared services. They verify equivalent games, dependencies, scripts, manifests, release records, formatted indexes, validation reports, and publish previews. +3. Integration tests use local bare Git repositories, controlled HTTP handlers, fake `git` and `gh` process results, and controlled SFTP boundaries. They verify successful publication, refusal paths, partial completion reporting, lock conflicts, cancellation, and live read-back behavior without touching the user's real repositories or server. + +All existing solution tests must remain green. New tests must cover both the standard build and the `RegistryAdmin=true` build. A disposable end-to-end catalog and package exercise must succeed before local installation. + +## Build and Local Delivery + +Add `installer/build-author-cli.ps1` with framework-dependent and self-contained Windows x64 modes matching the existing AuthorTool build conventions. Produce: + +- `amm-author.exe` +- `amm-author-admin.exe` when built with `RegistryAdmin=true` + +Install the local self-contained build under `C:\Users\buu42\Tools\AccessibilityModManager`. Add that directory to the current user's PATH only if it is not already present. Verify invocation from outside the source tree with `amm-author --version`, `amm-author --help`, and a read-only command against a disposable project. + +The local Git branch, commits, executable, and test artifacts remain on this machine. There will be no GitHub fork, push, pull request, release asset, or redistribution until the user explicitly authorizes it after speaking with RealAmethyst. + +## Non-Goals + +- Replacing or removing the WPF AuthorTool. +- Changing the plugin-index or manifest schema merely for CLI convenience. +- Adding security bypasses or alternative unsigned publishing paths. +- Automating the WPF interface. +- Publishing the local build or source changes. +- Modifying Blind Soldier as part of this work; the CLI is tooling for later releases. From a0aa63683b13b5d87fbcc7975ddfd689070ecbf6 Mon Sep 17 00:00:00 2001 From: buu420 <43735748+buu420@users.noreply.github.com> Date: Tue, 4 Aug 2026 20:07:26 -0500 Subject: [PATCH 02/12] docs: plan full AuthorTool CLI implementation --- .../plans/2026-08-04-full-author-cli.md | 1075 +++++++++++++++++ 1 file changed, 1075 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-04-full-author-cli.md diff --git a/docs/superpowers/plans/2026-08-04-full-author-cli.md b/docs/superpowers/plans/2026-08-04-full-author-cli.md new file mode 100644 index 0000000..d15523a --- /dev/null +++ b/docs/superpowers/plans/2026-08-04-full-author-cli.md @@ -0,0 +1,1075 @@ +# Full Accessibility Mod Manager Author CLI Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a local `amm-author.exe` with feature parity with Accessibility Mod Manager AuthorTool 0.28.0 while preserving the exact package, catalog, trust, signing, and publishing safeguards. + +**Architecture:** Move the AuthorTool's UI-independent services unchanged into a shared `AccessibilityModManager.Authoring` assembly, then add typed workflows that both the WPF application and a new `AccessibilityModManager.AuthorCli` console application can call. The CLI uses `System.CommandLine` 2.0.10, screen-reader-safe line output, JSON payloads for lossless complex model editing, existing AuthorTool configuration, and the existing security services. + +**Tech Stack:** C# 14, .NET 10 Windows, WPF, System.CommandLine 2.0.10, Microsoft.Extensions.DependencyInjection 10.0.3, Serilog 4.3.1, SSH.NET 2024.1.0, xUnit 2.9.3, Git and GitHub CLI. + +## Global Constraints + +- Start from upstream commit `9e2d223762aa21a2fc765bae55380699a2532746`, AuthorTool version 0.28.0. +- Keep the work local on branch `local/full-author-cli`; do not fork, push, open a pull request, create a release, or redistribute a binary. +- Do not weaken, bypass, replace, or add override switches for any package, path, manifest, HTTPS, identity, registry, signing, replay, host-key, or publish-lock check. +- Preserve `%LocalAppData%\AccessibilityModManager-Author\config.json` compatibility and DPAPI protection. +- Target `net10.0-windows` and publish Windows x64 executables. +- Standard builds exclude registry-admin execution; admin builds enable it only through the existing `RegistryAdmin=true` build property. +- Default output is complete plain-text lines without animation, cursor rewriting, color-only meaning, decorative tables, or unlabeled symbols. +- Passphrases are read through concealed input or `--passphrase-stdin`; they are never accepted as ordinary option values or logged. +- `--yes` confirms an action but never bypasses validation or trust gates. +- `--dry-run` performs reads and validation but never writes, commits, uploads, signs, changes gates, or removes locks. +- Use test-first development for every new behavior and run `dotnet test AccessibilityModManager.slnx` before each task commit. + +## File Structure + +### Shared authoring assembly + +- Create `src/AccessibilityModManager.Authoring/AccessibilityModManager.Authoring.csproj`. +- Move the 22 UI-independent files from `src/AccessibilityModManager.AuthorTool/Services/` to `src/AccessibilityModManager.Authoring/Services/` without changing their namespaces or behavior. +- Create `src/AccessibilityModManager.Authoring/Workflows/WorkflowResult.cs` for stable operation categories and typed results. +- Create `src/AccessibilityModManager.Authoring/Workflows/AuthorProjectContext.cs` for project resolution and locking. +- Create `src/AccessibilityModManager.Authoring/Workflows/JsonPayloadService.cs` for exact model import and export. +- Create `src/AccessibilityModManager.Authoring/Workflows/CatalogWorkflow.cs` for author, game, dependency, script, and release mutations. +- Create `src/AccessibilityModManager.Authoring/Workflows/PackageWorkflow.cs` for build and package validation. +- Create `src/AccessibilityModManager.Authoring/Workflows/ReleaseWorkflow.cs` for staged-byte validation and GitHub/server release publication. +- Create `src/AccessibilityModManager.Authoring/Workflows/IndexWorkflow.cs` for reconciliation, validation, saving, and destination publication. +- Create `src/AccessibilityModManager.Authoring/Workflows/PatreonWorkflow.cs` and `ServerWorkflow.cs`. +- Create `src/AccessibilityModManager.Authoring/Workflows/SigningWorkflow.cs` and `RegistryAdminWorkflow.cs`. + +### CLI assembly + +- Create `src/AccessibilityModManager.AuthorCli/AccessibilityModManager.AuthorCli.csproj`. +- Create `src/AccessibilityModManager.AuthorCli/Program.cs` and `CliServices.cs`. +- Create `src/AccessibilityModManager.AuthorCli/Console/CliConsole.cs`, `SecretReader.cs`, `OutcomeWriter.cs`, and `ExitCodes.cs`. +- Create one focused command-registration file for each top-level command under `src/AccessibilityModManager.AuthorCli/Commands/`. +- Create `src/AccessibilityModManager.AuthorCli/Properties/PublishProfiles/win-x64.pubxml`. + +### Existing WPF application + +- Modify `src/AccessibilityModManager.AuthorTool/AccessibilityModManager.AuthorTool.csproj` to reference Authoring. +- Modify `src/AccessibilityModManager.AuthorTool/App.xaml.cs` to register shared workflows. +- Modify author view models only where orchestration moves into a workflow; keep bindings, announcements, and dialog behavior unchanged. + +### Tests and delivery + +- Add `tests/AccessibilityModManager.Tests/Authoring/` workflow tests. +- Add `tests/AccessibilityModManager.Tests/AuthorCli/` parser, handler, output, and parity tests. +- Add `installer/build-author-cli.ps1`. +- Update `README.md` with local build and command documentation without linking to an unofficial binary. + +--- + +### Task 1: Extract the UI-independent Authoring Assembly + +**Files:** +- Create: `src/AccessibilityModManager.Authoring/AccessibilityModManager.Authoring.csproj` +- Move: `src/AccessibilityModManager.AuthorTool/Services/*.cs` to `src/AccessibilityModManager.Authoring/Services/*.cs` +- Modify: `src/AccessibilityModManager.AuthorTool/AccessibilityModManager.AuthorTool.csproj` +- Modify: `tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj` +- Modify: `AccessibilityModManager.slnx` +- Test: `tests/AccessibilityModManager.Tests/Authoring/AuthoringAssemblyTests.cs` + +**Interfaces:** +- Consumes: existing service types in namespace `AccessibilityModManager.AuthorTool.Services`. +- Produces: the same public service types from assembly `AccessibilityModManager.Authoring`, with unchanged names and signatures. + +- [ ] **Step 1: Record the clean baseline** + +Run: + +```powershell +dotnet test AccessibilityModManager.slnx --no-restore +``` + +Expected: all existing tests pass at commit `9e2d223` plus the design commit. + +- [ ] **Step 2: Write the failing assembly-boundary test** + +Create: + +```csharp +using AccessibilityModManager.AuthorTool.Services; + +namespace AccessibilityModManager.Tests.Authoring; + +public sealed class AuthoringAssemblyTests +{ + [Fact] + public void UiIndependentServicesLiveInAuthoringAssembly() + { + Assert.Equal("AccessibilityModManager.Authoring", + typeof(ManifestBuilderService).Assembly.GetName().Name); + Assert.Equal(typeof(ManifestBuilderService).Assembly, + typeof(IndexPublishCoordinator).Assembly); + Assert.Equal(typeof(ManifestBuilderService).Assembly, + typeof(ClaimSigningKeyStore).Assembly); + } +} +``` + +- [ ] **Step 3: Run the boundary test and verify failure** + +Run: + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter FullyQualifiedName~AuthoringAssemblyTests +``` + +Expected: failure because each service still lives in `AccessibilityModManager.AuthorTool`. + +- [ ] **Step 4: Create the shared project and move services without logic edits** + +Use this project definition: + +```xml + + + + + + + + + + + net10.0-windows + enable + enable + AccessibilityModManager.Authoring + + +``` + +Move every service file with its contents and `AccessibilityModManager.AuthorTool.Services` namespace unchanged. Add an Authoring project reference to the WPF and test projects, remove the WPF project's now-unused SSH.NET reference, and add the new project under `/src/` in `AccessibilityModManager.slnx`. + +- [ ] **Step 5: Run boundary and regression tests** + +Run: + +```powershell +dotnet test AccessibilityModManager.slnx +``` + +Expected: the new boundary test and every existing test pass. + +- [ ] **Step 6: Commit the pure extraction** + +```powershell +git add AccessibilityModManager.slnx src tests +git commit -m "refactor: share AuthorTool services" +``` + +--- + +### Task 2: Add CLI Results, Console I/O, Project Resolution, and Locking + +**Files:** +- Create: `src/AccessibilityModManager.Authoring/Workflows/WorkflowResult.cs` +- Create: `src/AccessibilityModManager.Authoring/Workflows/AuthorProjectContext.cs` +- Create: `src/AccessibilityModManager.Authoring/Workflows/JsonPayloadService.cs` +- Create: `src/AccessibilityModManager.AuthorCli/AccessibilityModManager.AuthorCli.csproj` +- Create: `src/AccessibilityModManager.AuthorCli/Program.cs` +- Create: `src/AccessibilityModManager.AuthorCli/CliServices.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Console/CliConsole.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Console/SecretReader.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Console/OutcomeWriter.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Console/ExitCodes.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/RootCommands.cs` +- Modify: `AccessibilityModManager.slnx` +- Modify: `tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/ProjectResolutionTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/OutcomeWriterTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/SecretReaderTests.cs` + +**Interfaces:** +- Produces: `WorkflowResult`, `WorkflowErrorKind`, `AuthorProjectContext.ResolveAsync`, `JsonPayloadService.ReadAsync`, `ICliConsole`, `CliExitCode`, and a runnable `amm-author --version`. +- Consumes: `AuthorConfigService`, `IndexFileService`, and `CrossProcessFileLock.AcquireAsync`. + +- [ ] **Step 1: Write project-resolution tests** + +Cover explicit path, current directory, last-opened project, missing project, and a project whose `index.json` is absent. The core assertion is: + +```csharp +var resolved = await context.ResolveAsync(explicitPath, currentDirectory, CancellationToken.None); +Assert.Equal(Path.GetFullPath(explicitPath), resolved.ProjectPath); +Assert.Equal("sample", resolved.Index.PluginId); +``` + +- [ ] **Step 2: Write output and secret-input tests** + +Verify human output uses complete lines, JSON output is a single valid object, errors do not enter standard output in JSON mode, and concealed input handles backspace without echoing characters: + +```csharp +var result = new WorkflowResult("ok", "value", []); +writer.Write(result, json: true); +Assert.Equal("{\"status\":\"ok\",\"value\":\"value\",\"messages\":[]}" + Environment.NewLine, + console.Stdout); +Assert.DoesNotContain("secret", console.AllWrittenText); +``` + +- [ ] **Step 3: Run the new tests and verify compilation failure** + +Run: + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~ProjectResolutionTests|FullyQualifiedName~OutcomeWriterTests|FullyQualifiedName~SecretReaderTests" +``` + +Expected: failure because the workflow and CLI types do not exist. + +- [ ] **Step 4: Implement stable result and exit contracts** + +Define: + +```csharp +public enum WorkflowErrorKind { None, Usage, Validation, Authentication, Conflict, Cancelled } + +public sealed record WorkflowResult( + string Status, + T? Value, + IReadOnlyList Messages, + WorkflowErrorKind ErrorKind = WorkflowErrorKind.None, + IReadOnlyList? CompletedPhases = null); + +public enum CliExitCode +{ + Success = 0, + Usage = 2, + Validation = 3, + Authentication = 4, + Conflict = 5, + Cancelled = 130 +} + +public sealed record ResolvedAuthorProject(string ProjectPath, PluginRepoIndex Index); + +public interface ICliConsole +{ + TextReader In { get; } + TextWriter Out { get; } + TextWriter Error { get; } + bool IsInputRedirected { get; } + void WriteStatus(string message); +} +``` + +Map `WorkflowErrorKind` to the exact exit codes above. + +- [ ] **Step 5: Implement project resolution and a project lease** + +`AuthorProjectContext.ResolveAsync(string? explicitPath, string currentDirectory, CancellationToken ct)` returns `ResolvedAuthorProject` and must apply explicit path, current directory, then saved project ordering. `AcquireWriteLeaseAsync(string projectPath, CancellationToken ct)` returns the `FileStream` from a `.amm-author.lock` file under the project folder through `CrossProcessFileLock.AcquireAsync`. Read-only commands and every `--dry-run` path do not acquire the write lease or create a lock file. + +- [ ] **Step 6: Implement JSON payload and console primitives** + +`JsonPayloadService.ReadAsync(string source, TextReader stdin, CancellationToken ct)` accepts a UTF-8 file or `-` for standard input, uses camelCase JSON options, and rejects null documents. `SecretReader.ReadAsync(ICliConsole console, CancellationToken ct)` reads one key at a time, masks nothing, echoes nothing, supports backspace, ends on Enter, and throws `OperationCanceledException` on Ctrl+C. + +- [ ] **Step 7: Create the console host** + +Use this CLI project core: + +```xml + + + + + + + + + + + + + + Exe + net10.0-windows + enable + enable + amm-author + 0.28.0 + + + $(DefineConstants);REGISTRY_ADMIN + + +``` + +Register `--version`, `--json`, `--quiet`, `--project`, `--dry-run`, and `--yes` at the root. Catch `OperationCanceledException` and return 130; map typed workflow errors without stack traces unless `--verbose` is present. + +- [ ] **Step 8: Run focused and full tests** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~AuthorCli" +dotnet test AccessibilityModManager.slnx +dotnet run --project src/AccessibilityModManager.AuthorCli -- --version +``` + +Expected: focused and full tests pass; version output is `0.28.0`. + +- [ ] **Step 9: Commit the CLI foundation** + +```powershell +git add AccessibilityModManager.slnx src tests +git commit -m "feat: add Author CLI foundation" +``` + +--- + +### Task 3: Implement Project, Author, Game, Dependency, and Script Commands + +**Files:** +- Create: `src/AccessibilityModManager.Authoring/Workflows/CatalogWorkflow.cs` +- Create: `src/AccessibilityModManager.Authoring/Workflows/DependencyPresetCatalog.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/ProjectCommands.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/AuthorCommands.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/GameCommands.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/DependencyCommands.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/ScriptCommands.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/CatalogWorkflowTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/CatalogCommandTests.cs` + +**Interfaces:** +- Produces: `CatalogWorkflow.CreateProject`, `SetAuthor`, `AddGame`, `UpdateGame`, `RemoveGame`, `UpsertDependency`, `RemoveDependency`, `SetLifecycleScript`, and `ClearLifecycleScript`. +- Consumes: `PluginRepoIndex`, `GameDefinition`, `Dependency`, `LifecycleScript`, `IndexFileService`, `JsonPayloadService`, and `AuthorProjectContext`. + +- [ ] **Step 1: Write lossless catalog-mutation tests** + +Create complete model fixtures containing tags, languages, probe rules, registry probes, ASCII path shims, dependency checks, fixes, auto-install actions, version discovery, and all lifecycle script fields. Assert round-trip preservation and targeted mutation: + +```csharp +var changed = workflow.UpsertDependency(index, "ffviinew", replacement); +Assert.Equal(replacement, changed.Games.Single(g => g.GameId == "ffviinew").Dependencies.Single(d => d.Id == replacement.Id)); +Assert.Equal(original.ReleasesByGameId, changed.ReleasesByGameId); +``` + +Also assert duplicate game ids and duplicate dependency ids are rejected case-insensitively. + +- [ ] **Step 2: Write command tests for human and JSON input** + +Exercise `project init`, `project status`, `author set --input`, `game add --input`, `game update --input`, `game remove`, `dependency set --input`, `dependency remove`, `script set --input`, and `script clear`. Assert `--dry-run` leaves `index.json` byte-identical. + +- [ ] **Step 3: Run focused tests and verify failure** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~CatalogWorkflowTests|FullyQualifiedName~CatalogCommandTests" +``` + +Expected: failure because catalog workflows and commands are absent. + +- [ ] **Step 4: Implement immutable catalog mutations** + +Each method returns a new `PluginRepoIndex` and preserves unrelated data. Define exact signatures: + +```csharp +public PluginRepoIndex SetAuthor(PluginRepoIndex index, PluginAuthorInfo? author); +public PluginRepoIndex CreateProject(string pluginId); +public PluginRepoIndex AddGame(PluginRepoIndex index, GameDefinition game); +public PluginRepoIndex UpdateGame(PluginRepoIndex index, string currentGameId, GameDefinition replacement); +public PluginRepoIndex RemoveGame(PluginRepoIndex index, string gameId); +public PluginRepoIndex UpsertDependency(PluginRepoIndex index, string gameId, Dependency dependency); +public PluginRepoIndex RemoveDependency(PluginRepoIndex index, string gameId, string dependencyId); +public PluginRepoIndex SetLifecycleScript(PluginRepoIndex index, string gameId, LifecycleSlot slot, LifecycleScript script); +public PluginRepoIndex ClearLifecycleScript(PluginRepoIndex index, string gameId, LifecycleSlot slot); + +public enum LifecycleSlot { PreInstall, PostInstall, PostUninstall } +``` + +On a game-id rename, update the `Games` entry and `ReleasesByGameId` key, but refuse releases whose embedded `GameId` would no longer match unless the caller passes `--rewrite-release-game-id` and confirms the preview. + +- [ ] **Step 5: Register project and catalog commands** + +Use `--input ` for complete camelCase models so every current and future field remains expressible. Add `project init`, `recent`, `open`, `clone`, `pull`, `repos`, and `status`; add `author show` and `set`; add complete game, dependency, and script CRUD. Move the current dependency preset definitions from `ViewModels/DependencyPresets.cs` into `DependencyPresetCatalog`, make the WPF view model consume that catalog, and add `dependency presets` plus `dependency apply-preset`. Add concise flags for common game fields: `--id`, `--display-name`, `--mod-name`, `--description`, `--steam-app-id`, `--exe-name`, repeated `--tag`, and repeated `--language`. When both an input document and field flags are supplied, reject the command with exit code 2. + +- [ ] **Step 6: Validate before every durable save** + +Serialize the candidate index, run `PluginIndexValidation.Validate(candidate.PluginId, json)`, and refuse any `PublishBlockers`. There is no CLI flag that saves a candidate rejected by the shared validator. + +- [ ] **Step 7: Run tests and command smoke checks** + +```powershell +dotnet test AccessibilityModManager.slnx +dotnet run --project src/AccessibilityModManager.AuthorCli -- project --help +dotnet run --project src/AccessibilityModManager.AuthorCli -- game --help +dotnet run --project src/AccessibilityModManager.AuthorCli -- dependency --help +dotnet run --project src/AccessibilityModManager.AuthorCli -- script --help +``` + +Expected: all tests pass and each help command lists its complete subcommands. + +- [ ] **Step 8: Commit catalog authoring** + +```powershell +git add src tests +git commit -m "feat: add catalog authoring commands" +``` + +--- + +### Task 4: Implement Wrapped Package Build and Validation Commands + +**Files:** +- Create: `src/AccessibilityModManager.Authoring/Workflows/PackageWorkflow.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/PackageCommands.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/PackageWorkflowTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/PackageCommandTests.cs` + +**Interfaces:** +- Produces: `PackageBuildRequest`, `PackageInspection`, `PackageWorkflow.BuildAsync`, and `PackageWorkflow.ValidateAsync`. +- Consumes: `ManifestBuilderService`, `PluginPackageValidation`, `Sha256HashService`, catalog dependencies, and lifecycle-script source mappings. + +- [ ] **Step 1: Write package behavior tests** + +Cover a file-only mod, folder content, an external lifecycle script, a script-only mod, mismatched game/plugin/version identity, a missing script, unsafe script paths, and cancellation. Assert validation reads the staged stream and returns its exact SHA256: + +```csharp +var result = await workflow.BuildAsync(request, CancellationToken.None); +Assert.True(result.Validation.IsValid); +Assert.Equal(Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(result.ZipPath))), result.Sha256); +``` + +- [ ] **Step 2: Write package command tests** + +Cover `package build`, `package validate`, and `package hash`. Verify `package build --dry-run` validates source and output paths without creating a ZIP. + +- [ ] **Step 3: Run focused tests and verify failure** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~PackageWorkflowTests|FullyQualifiedName~PackageCommandTests" +``` + +Expected: failure because package workflows and commands are absent. + +- [ ] **Step 4: Implement package workflow** + +Define: + +```csharp +public sealed record PackageBuildRequest( + string SourceFolder, + string OutputZipPath, + string PluginId, + string GameId, + string Version, + IReadOnlyList Dependencies, + LifecycleScriptInputs Scripts); + +public sealed record PackageInspection( + string ZipPath, + string Sha256, + int FileCount, + long TotalBytes, + PackageValidationReport Validation); +``` + +Build through `ManifestBuilderService`, reopen the finished ZIP read-only, validate through `PluginPackageValidation`, calculate the digest from that finished file, and delete the output if validation fails. + +- [ ] **Step 5: Register package commands** + +`package build` accepts `--source`, `--game`, `--version`, optional `--output`, and resolves plugin id, dependencies, and scripts from the project. `package validate` requires `--zip`, `--plugin`, `--game`, and `--version`. `package hash` outputs only the lowercase digest in normal mode and a named property in JSON mode. + +- [ ] **Step 6: Run all tests and inspect a disposable ZIP** + +```powershell +dotnet test AccessibilityModManager.slnx +dotnet run --project src/AccessibilityModManager.AuthorCli -- package build --project --source --game sample --version 1.0.0 +``` + +Expected: the package contains root `manifest.json` and content under `files/`; validation succeeds. + +- [ ] **Step 7: Commit package authoring** + +```powershell +git add src tests +git commit -m "feat: add package authoring commands" +``` + +--- + +### Task 5: Implement GitHub Release and Asset Publication + +**Files:** +- Create: `src/AccessibilityModManager.Authoring/Workflows/ReleaseWorkflow.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/GitHubCommands.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/ReleaseCommands.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/ReleaseWorkflowTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/ReleaseCommandTests.cs` + +**Interfaces:** +- Produces: `ReleasePublishRequest`, `ReleasePublishPreview`, `ReleasePublishResult`, `ReleaseWorkflow.PrepareAsync`, and `ReleaseWorkflow.PublishAsync`. +- Consumes: `GitHubService`, `PluginPackageValidation`, `Sha256HashService`, `CatalogWorkflow`, `AuthorConfigService`, and a read-locked staged package. + +- [ ] **Step 1: Write staged-byte and partial-result tests** + +Use a fake GitHub service boundary to prove the bytes hashed are the bytes uploaded, an existing tag edits notes rather than creating a second release, an existing asset is replaced only after confirmation, private repositories are refused, and upload success plus catalog-save failure returns completed phase `githubAssetUploaded` with exit category Conflict. + +- [ ] **Step 2: Write release command tests** + +Cover `release list`, `release show`, `release add --input`, `release edit --input`, `release remove`, `release upload`, and `release publish`. Verify release identity is `(version, channel)` and edits remove the old identity before inserting the new one. + +- [ ] **Step 3: Run focused tests and verify failure** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~ReleaseWorkflowTests|FullyQualifiedName~ReleaseCommandTests" +``` + +Expected: failure because release workflows and commands are absent. + +- [ ] **Step 4: Extract staging from the WPF release view model** + +Move the current `StagedPackage` behavior into Authoring as an internal disposable type. Preserve leaf-name validation, private temporary directory creation, write exclusion, digest calculation, and cleanup byte-for-byte. Replace the WPF view model's private implementation with `ReleaseWorkflow.PrepareAsync`. + +- [ ] **Step 5: Implement GitHub release workflow** + +Define: + +```csharp +public sealed record ReleasePublishRequest( + string ProjectPath, + string PluginId, + string GameId, + string Version, + string Channel, + string SourceRepo, + string LocalZipPath, + string? AssetFileName, + string? Notes, + string? ChangelogUrl, + PatreonGate? Patreon); + +public sealed record ReleasePublishPreview( + string Repository, + string Tag, + string AssetFileName, + string Sha256, + bool CreatesRelease, + bool ReplacesAsset); + +public sealed record ReleasePublishResult( + ModRelease Release, + string AssetUrl, + string Sha256, + IReadOnlyList CompletedPhases); + +public sealed class PreparedRelease : IAsyncDisposable +{ + public ReleasePublishPreview Preview { get; } + public string StagedPath { get; } + public string Sha256 { get; } + public ValueTask DisposeAsync(); +} + +public Task> PrepareAsync( + ReleasePublishRequest request, CancellationToken ct); + +public Task> PublishAsync( + PreparedRelease prepared, ReleasePublishRequest request, bool confirmed, CancellationToken ct); +``` + +Preparation validates all metadata and package identity before upload. Publication uses the existing `GitHubService` methods and returns the public asset URL plus exact staged SHA256. It does not save or publish `index.json`; Task 7 composes that transaction. + +- [ ] **Step 6: Register GitHub and release commands** + +Add `github status`, `github repos`, and `github releases --repo`. Add complete release CRUD plus `release upload`. `release publish` is registered now but reports a clear unavailable-phase result until Task 7 supplies index publication; its parser contract is pinned by tests. + +- [ ] **Step 7: Run regression and disposable fake-publish tests** + +```powershell +dotnet test AccessibilityModManager.slnx +``` + +Expected: every test passes and no real GitHub repository was modified. + +- [ ] **Step 8: Commit release publication** + +```powershell +git add src tests +git commit -m "feat: add GitHub release authoring" +``` + +--- + +### Task 6: Implement Index Reconciliation, Validation, and Publication + +**Files:** +- Create: `src/AccessibilityModManager.Authoring/Workflows/IndexWorkflow.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/IndexCommands.cs` +- Modify: `src/AccessibilityModManager.AuthorCli/Commands/ReleaseCommands.cs` +- Modify: `src/AccessibilityModManager.AuthorTool/ViewModels/IndexEditorViewModel.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/IndexWorkflowTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/IndexCommandTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/CompleteReleasePublishTests.cs` + +**Interfaces:** +- Produces: `IndexPublishRequest`, `IndexPublishPreview`, `IndexPublishResult`, `IndexWorkflow.Validate`, `ReconcileAsync`, `SaveAsync`, `PublishAsync`, `InspectLockAsync`, and `BreakLockAsync`. +- Consumes: `ProjectReconciler`, `IndexPublishCoordinator`, `GitHubIndexPublisher`, `UnsignedPublishGate`, `RegistryMembershipChecker`, `ServerUploadService`, and `AuthorConfigService` publishing records. + +- [ ] **Step 1: Write reconciliation and publish tests** + +Cover local-equals-last-published, live advanced elsewhere, local unpublished edits, unreadable live state, registered URL mismatch, private GitHub repository, missing destination, signed catalog on an unsigned path, GitHub publish, server publish, lock contention, compare-before-break, and read-back mismatch. + +- [ ] **Step 2: Write complete release transaction tests** + +Assert this exact phase order: + +```csharp +Assert.Equal(new[] { + "projectLocked", "catalogReconciled", "packageValidated", "assetUploaded", + "releaseRecorded", "indexValidated", "indexSaved", "indexPublished", "liveVerified" +}, result.CompletedPhases); +``` + +Simulate failure after each phase and verify the result lists only phases that actually completed. + +- [ ] **Step 3: Run focused tests and verify failure** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~IndexWorkflowTests|FullyQualifiedName~IndexCommandTests|FullyQualifiedName~CompleteReleasePublishTests" +``` + +Expected: failure because index workflow and composed release publication are absent. + +- [ ] **Step 4: Extract index orchestration into the shared workflow** + +Move non-UI logic from `IndexEditorViewModel` into `IndexWorkflow` while leaving UI messages and prompts in the view model. The workflow must return a preview before mutation and accept a separate confirmed execution call. Preserve the existing second authorization check immediately before GitHub push. + +Use these contracts: + +```csharp +public sealed record IndexPublishRequest( + string ProjectPath, + PluginRepoIndex Candidate, + PublishDestination Destination, + string CommitMessage, + bool DryRun); + +public sealed record IndexPublishPreview( + string PluginId, + PublishDestination Destination, + string DestinationDescription, + string CommitMessage, + IReadOnlyList CatalogChanges); + +public sealed record IndexPublishResult( + string PluginId, + string PublishedSha256, + string DestinationDescription, + IReadOnlyList CompletedPhases); + +public IndexValidationReport Validate(PluginRepoIndex candidate); +public Task> ReconcileAsync(string projectPath, CancellationToken ct); +public Task> SaveAsync(string projectPath, PluginRepoIndex candidate, bool dryRun, CancellationToken ct); +public Task> PreviewPublishAsync(IndexPublishRequest request, CancellationToken ct); +public Task> PublishAsync(IndexPublishRequest request, bool confirmed, CancellationToken ct); +public Task> InspectLockAsync(string pluginId, CancellationToken ct); +public Task> BreakLockAsync(string pluginId, string expectedFingerprint, bool confirmed, CancellationToken ct); +``` + +- [ ] **Step 5: Implement index commands** + +Add `index show`, `index validate`, `index reconcile`, `index save`, `index destination get`, `index destination set`, `index membership`, `index publish`, `index lock show`, and `index lock break`. `index lock break` requires the displayed lock fingerprint and confirmation; if the lock changes, it refuses. + +- [ ] **Step 6: Complete `release publish` composition** + +Acquire the project lease, reconcile, prepare and validate the package, publish the asset, add the release in memory, validate and durably save the index, publish the selected destination, verify the live index, and record the published digest. Emit a phase line after each completed phase. + +- [ ] **Step 7: Run all tests and local Git integration** + +```powershell +dotnet test AccessibilityModManager.slnx +``` + +Use a disposable working repository with a local bare remote to verify commit, push, branch creation, CRLF normalization behavior, and live blob read-back without network writes. + +- [ ] **Step 8: Commit index publication** + +```powershell +git add src tests +git commit -m "feat: add safe catalog publication" +``` + +--- + +### Task 7: Implement Patreon and SFTP Server Parity + +**Files:** +- Create: `src/AccessibilityModManager.Authoring/Workflows/PatreonWorkflow.cs` +- Create: `src/AccessibilityModManager.Authoring/Workflows/ServerWorkflow.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/PatreonCommands.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/ServerCommands.cs` +- Modify: `src/AccessibilityModManager.Authoring/Workflows/ReleaseWorkflow.cs` +- Modify: `src/AccessibilityModManager.Authoring/Workflows/IndexWorkflow.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/PatreonWorkflowTests.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/ServerWorkflowTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/PatreonServerCommandTests.cs` + +**Interfaces:** +- Produces: Patreon session, tier, post, and attachment results; server configuration, self-test, release upload, gate update, and lock results. +- Consumes: `PatreonAuthorService`, `ServerUploadService`, `ServerSelfTest`, and DPAPI-aware `AuthorConfigService`. + +- [ ] **Step 1: Write Patreon command and workflow tests** + +Cover signed-out status, sign-in cancellation, sign-out, tier refresh, invalid post URL, numeric post extraction, attachment selection, no selected tiers, no campaign id, and both Patreon-post and author-server delivery modes. + +- [ ] **Step 2: Write server command and workflow tests** + +Cover configuration validation, missing key, host-key mismatch, connection test steps, public upload, gated upload, refusing different bytes at an existing version, gate-only update, gate removal after catalog publication, publish-lock inspection, and changed-lock refusal. + +- [ ] **Step 3: Run focused tests and verify failure** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~PatreonWorkflowTests|FullyQualifiedName~ServerWorkflowTests|FullyQualifiedName~PatreonServerCommandTests" +``` + +Expected: failure because the workflows and commands are absent. + +- [ ] **Step 4: Implement Patreon workflow and commands** + +Add `patreon status`, `login`, `logout`, `tiers`, and `post validate`. Browser-based OAuth remains the existing service behavior. `post validate` outputs every attachment with its file name and stable selection id. + +Use these contracts: + +```csharp +public sealed record PatreonSessionStatus(bool IsSignedIn, string? MemberName, string? CampaignId); +public sealed record PatreonTierInfo(string TierId, string DisplayName); +public sealed record PatreonAttachmentInfo(string SelectionId, string FileName, string? DownloadUrl); +public sealed record PatreonPostInspection(string PostId, IReadOnlyList Attachments); + +public Task> GetStatusAsync(CancellationToken ct); +public Task> SignInAsync(CancellationToken ct); +public Task> SignOutAsync(CancellationToken ct); +public Task>> GetTiersAsync(CancellationToken ct); +public Task> InspectPostAsync(string postUrl, CancellationToken ct); +``` + +- [ ] **Step 5: Implement server workflow and secret-safe configuration** + +Add `server status`, `configure`, `clear`, `test`, `self-test`, `release inspect`, `release upload`, `gate set`, `gate remove`, `lock show`, and `lock break`. Read the SSH key passphrase only through concealed input or `--passphrase-stdin`, then persist it through the existing DPAPI-aware configuration service. + +Wrap the existing sealed service behind a testable adapter without changing it: + +```csharp +public sealed record ServerConfigurationInput(ServerUploadConfig Config, string KeyPassphrase); +public sealed record ServerConnectionReport(bool Connected, IReadOnlyList Steps); +public sealed record ServerReleaseRequest( + string GameId, string Version, string AssetFileName, string LocalZipPath, PatreonGate? Gate); + +public interface IServerAuthorTransport +{ + Task TestAsync(ServerUploadConfig config, CancellationToken ct); + Task PublishReleaseAsync( + ServerUploadConfig config, ServerReleaseRequest request, CancellationToken ct); + Task PublishGateAsync(ServerUploadConfig config, string gameId, string version, PatreonGate gate, CancellationToken ct); + Task RemoveGateAsync(ServerUploadConfig config, string gameId, string version, CancellationToken ct); +} +``` + +- [ ] **Step 6: Integrate gated release sequencing** + +For a gated release, upload the package and fresh gate first, publish the index second, then apply a changed or removed gate only after the live catalog matches. Preserve the WPF flow's public-URL reachability check when removing a gate. + +- [ ] **Step 7: Run all tests** + +```powershell +dotnet test AccessibilityModManager.slnx +``` + +Expected: all tests pass and test configuration stays under disposable override directories. + +- [ ] **Step 8: Commit Patreon and server parity** + +```powershell +git add src tests +git commit -m "feat: add Patreon and server authoring" +``` + +--- + +### Task 8: Implement Signing and Registry-Admin Parity + +**Files:** +- Create: `src/AccessibilityModManager.Authoring/Workflows/AuthoringBuildFlags.cs` +- Create: `src/AccessibilityModManager.Authoring/Workflows/SigningWorkflow.cs` +- Create: `src/AccessibilityModManager.Authoring/Workflows/RegistryAdminWorkflow.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/SigningCommands.cs` +- Create: `src/AccessibilityModManager.AuthorCli/Commands/RegistryCommands.cs` +- Modify: `src/AccessibilityModManager.AuthorTool/BuildFlags.cs` +- Modify: `src/AccessibilityModManager.AuthorTool/App.xaml.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/SigningWorkflowTests.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/RegistryAdminWorkflowTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/SigningRegistryCommandTests.cs` + +**Interfaces:** +- Produces: signing-key lifecycle operations, claim previews and signatures, publisher-head operations, registry validation and signature operations, and build-gated registry commands. +- Consumes: `ClaimSigningKeyStore`, `PublisherHeadStore`, `IndexProofService`, `ClaimSetBuilder`, `ClaimSigner`, `ServerUploadService`, `GitService`, and `GitHubService`. + +- [ ] **Step 1: Write signing lifecycle tests** + +Cover create, status, export, import, wrong passphrase, passphrase change, public-key mismatch, imported recordless key refusal, pending publish recovery, confirmation, and head reconciliation. Assert secrets never appear in workflow messages. + +- [ ] **Step 2: Write standard/admin build-gate tests** + +In a standard build, `registry status` must return exit code 4 with the explanation that an admin build is required. In an admin build, parser and workflow tests cover open/clone, refresh, JSON validation, signature creation, registry-pair upload, read-back, commit, and push. + +- [ ] **Step 3: Run focused tests and verify failure** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~SigningWorkflowTests|FullyQualifiedName~RegistryAdminWorkflowTests|FullyQualifiedName~SigningRegistryCommandTests" +``` + +Expected: failure because signing and registry workflows are absent. + +- [ ] **Step 4: Implement shared build flag** + +Define: + +```csharp +public static class AuthoringBuildFlags +{ +#if REGISTRY_ADMIN + public const bool IsRegistryAdmin = true; +#else + public const bool IsRegistryAdmin = false; +#endif +} +``` + +Make the WPF `BuildFlags.IsRegistryAdmin` delegate to this value. Pass `RegistryAdmin=$(RegistryAdmin)` to both Authoring and CLI projects in build commands. + +- [ ] **Step 5: Implement signing commands** + +Add `signing status`, `create`, `export`, `import`, `change-passphrase`, `claims preview`, `claims sign`, `head status`, `head confirm`, `head commit-pending`, and `head resume`. Secret input follows the global rule, and commands call existing stores and proof services without alternate validation. + +Use these workflow contracts: + +```csharp +public sealed record SigningKeyStatus( + string PluginId, string KeyId, string PublicKeyFingerprint, bool ImportedFromBackup, bool HasPublisherHead); +public sealed record ClaimPublishPreview( + string PluginId, string KeyId, long PublishNumber, IReadOnlyList Changes, string DeletionsToken); + +public WorkflowResult GetStatus(string pluginId); +public WorkflowResult Create(string pluginId, string passphrase); +public WorkflowResult Export(string pluginId, string destination, string exportPassphrase); +public WorkflowResult Import(string source, string importPassphrase); +public WorkflowResult ChangePassphrase(string pluginId, string currentPassphrase, string newPassphrase); +public Task> PreviewClaimsAsync(string projectPath, CancellationToken ct); +public Task> SignClaimsAsync( + string projectPath, string deletionsToken, bool confirmed, CancellationToken ct); +``` + +- [ ] **Step 6: Implement registry-admin commands** + +Add `registry status`, `open`, `refresh`, `json show`, `json validate`, `json save`, `sign`, `publish`, `commit`, and `push`. Standard builds register the group so help remains discoverable but every handler refuses before reading private configuration. + +Use these workflow contracts: + +```csharp +public sealed record RegistryDocumentResult(string Path, string Sha256, bool SignaturePresent); +public sealed record RegistryPublishResult( + string Destination, string JsonSha256, string SignatureSha256, IReadOnlyList CompletedPhases); + +public WorkflowResult Validate(string registryJsonPath); +public WorkflowResult Sign( + string registryJsonPath, string privateKeyPath, string passphrase, bool confirmed); +public Task> PublishAsync( + string registryRepoPath, bool confirmed, CancellationToken ct); +public Task> CommitAsync( + string registryRepoPath, string message, CancellationToken ct); +public Task> PushAsync(string registryRepoPath, CancellationToken ct); +``` + +- [ ] **Step 7: Run both build variants and all tests** + +```powershell +dotnet build AccessibilityModManager.slnx +dotnet build src/AccessibilityModManager.AuthorCli/AccessibilityModManager.AuthorCli.csproj -p:RegistryAdmin=true +dotnet test AccessibilityModManager.slnx +dotnet test AccessibilityModManager.slnx -p:RegistryAdmin=true +``` + +Expected: both builds succeed and all tests pass. + +- [ ] **Step 8: Commit signing and registry parity** + +```powershell +git add src tests +git commit -m "feat: add signing and registry CLI parity" +``` + +--- + +### Task 9: Complete Command Discovery, Help, JSON, and WPF Parity + +**Files:** +- Create: `src/AccessibilityModManager.AuthorCli/Commands/CommandCatalog.cs` +- Modify: all files under `src/AccessibilityModManager.AuthorCli/Commands/` +- Modify: `src/AccessibilityModManager.AuthorTool/App.xaml.cs` +- Modify: affected AuthorTool view models under `src/AccessibilityModManager.AuthorTool/ViewModels/` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/CommandCoverageTests.cs` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/AccessibilityOutputTests.cs` +- Test: `tests/AccessibilityModManager.Tests/Authoring/GuiCliParityTests.cs` + +**Interfaces:** +- Produces: a complete searchable command tree, stable JSON envelopes, and shared GUI/CLI workflow decisions. +- Consumes: every workflow from Tasks 2 through 8. + +- [ ] **Step 1: Write command-coverage tests from a required inventory** + +Pin this top-level inventory: + +```csharp +var required = new[] { + "project", "author", "game", "dependency", "script", "package", "release", + "index", "github", "patreon", "server", "signing", "registry" +}; +Assert.Equal(required, CommandCatalog.TopLevelNames); +``` + +For every group, assert all subcommands listed in the design are present and have a nonempty description and example. + +- [ ] **Step 2: Write accessibility-output tests** + +Reject carriage-return progress rewriting, ANSI escape sequences by default, messages whose only content is punctuation, and JSON mode with multiple standard-output documents. Verify `--quiet` still emits warnings and failures. + +- [ ] **Step 3: Write GUI/CLI parity tests** + +Feed the same fixture and confirmed preview into the shared workflow from a WPF-facing adapter and a CLI-facing adapter. Assert equal candidate index bytes, package validation reports, release metadata, destination decisions, and completed phases. + +- [ ] **Step 4: Run focused tests and verify failures** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter "FullyQualifiedName~CommandCoverageTests|FullyQualifiedName~AccessibilityOutputTests|FullyQualifiedName~GuiCliParityTests" +``` + +Expected: failures identify unregistered commands, incomplete help, or remaining GUI-only orchestration. + +- [ ] **Step 5: Finish command registration and examples** + +Centralize registration in `CommandCatalog` and make each group expose `Create(IServiceProvider services)`. Add examples to help text using Windows quoting and absolute-path examples that do not contain this user's private paths. + +- [ ] **Step 6: Finish WPF workflow adoption** + +Replace remaining duplicated non-UI decisions in WPF view models with shared workflow calls. Keep existing observable properties, command names, dialog text, focus behavior, and screen-reader announcements intact. + +- [ ] **Step 7: Run all tests and help snapshot** + +```powershell +dotnet test AccessibilityModManager.slnx +dotnet run --project src/AccessibilityModManager.AuthorCli -- --help +dotnet run --project src/AccessibilityModManager.AuthorCli -- release publish --help +``` + +Expected: all tests pass; help is complete and readable as plain text. + +- [ ] **Step 8: Commit parity and help** + +```powershell +git add src tests +git commit -m "feat: complete AuthorTool CLI parity" +``` + +--- + +### Task 10: Build, Document, Install, and Verify the Local CLI + +**Files:** +- Create: `installer/build-author-cli.ps1` +- Create: `src/AccessibilityModManager.AuthorCli/Properties/PublishProfiles/win-x64.pubxml` +- Modify: `README.md` +- Test: `tests/AccessibilityModManager.Tests/AuthorCli/PublishedCliSmokeTests.cs` + +**Interfaces:** +- Produces: local `amm-author.exe`, optional `amm-author-admin.exe`, build hashes, PATH installation, and documented commands. +- Consumes: completed CLI and existing .NET 10 SDK. + +- [ ] **Step 1: Write published-binary smoke test** + +The test launches a supplied published executable with `--version`, `--help`, and `project status --project --json`, then asserts exit code 0, version 0.28.0, parseable JSON, and no WPF process or window requirement. + +- [ ] **Step 2: Run the smoke test and verify it skips or fails without a published path** + +```powershell +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter FullyQualifiedName~PublishedCliSmokeTests +``` + +Expected: an explicit skipped result when `AMM_AUTHOR_CLI_EXE` is absent; the test never guesses a binary path. + +- [ ] **Step 3: Add the build script** + +The script accepts `-Configuration`, `-Version`, `-SelfContained`, and `-Admin`, reads version 0.28.0 from the CLI project when omitted, publishes `win-x64` single-file output, writes standard and admin builds to separate local folders, and prints a lowercase SHA256. It does not create or upload a GitHub release. + +- [ ] **Step 4: Add documentation** + +Document installation, project resolution, every command group, human versus JSON output, secret input, exit codes, dry-run and confirmation behavior, standard versus registry-admin builds, local-only license restriction, and five complete examples: inspect project, build package, validate package, publish a release, and dry-run an index publish. + +- [ ] **Step 5: Build both local variants** + +```powershell +powershell -ExecutionPolicy Bypass -File installer/build-author-cli.ps1 -SelfContained +powershell -ExecutionPolicy Bypass -File installer/build-author-cli.ps1 -SelfContained -Admin +``` + +Expected: `amm-author.exe` and `amm-author-admin.exe` are produced in separate local dist folders with hashes. + +- [ ] **Step 6: Run complete verification** + +```powershell +dotnet test AccessibilityModManager.slnx +$env:AMM_AUTHOR_CLI_EXE = (Resolve-Path 'dist-author-cli/amm-author.exe') +dotnet test tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj --filter FullyQualifiedName~PublishedCliSmokeTests +``` + +Expected: all solution and published-binary tests pass. + +- [ ] **Step 7: Install locally and update user PATH** + +Copy the two executables and their `.sha256` files to `C:\Users\buu42\Tools\AccessibilityModManager`. Add that exact directory to the current-user PATH only when its case-insensitive normalized entry is absent. Do not copy source or binaries into Blind Soldier, `buu-s-mods`, or any GitHub checkout intended for publication. + +- [ ] **Step 8: Verify outside the source tree** + +Run from `C:\Users\buu42`: + +```powershell +amm-author --version +amm-author --help +amm-author project status --project --json +``` + +Expected: version 0.28.0, complete help, and a successful JSON status result. + +- [ ] **Step 9: Confirm no external publication occurred** + +```powershell +git status --short +git log --oneline --decorate -12 +git remote -v +``` + +Expected: only local commits exist on `local/full-author-cli`; the remote remains read-only upstream and no push was performed. + +- [ ] **Step 10: Commit build and documentation** + +```powershell +git add installer src/AccessibilityModManager.AuthorCli/Properties README.md tests +git commit -m "build: package local Author CLI" +``` + +## Final Verification Checklist + +- [ ] `dotnet build AccessibilityModManager.slnx` succeeds. +- [ ] `dotnet build src/AccessibilityModManager.AuthorCli/AccessibilityModManager.AuthorCli.csproj -p:RegistryAdmin=true` succeeds. +- [ ] `dotnet test AccessibilityModManager.slnx` passes without skipped behavioral tests. +- [ ] Standard CLI refuses registry administration with exit code 4 and a readable explanation. +- [ ] Admin CLI runs registry read-only status against a disposable fixture. +- [ ] Human output contains no ANSI sequences or rewritten lines. +- [ ] JSON mode writes exactly one result object to standard output. +- [ ] Secrets do not appear in process arguments, stdout, stderr, or logs. +- [ ] `--dry-run` leaves fixture files and Git history byte-identical. +- [ ] Package build and validation succeed for a disposable mod. +- [ ] Complete release publication succeeds against fake GitHub and local Git boundaries. +- [ ] Patreon and SFTP flows pass controlled integration tests without touching live accounts or servers. +- [ ] Existing WPF AuthorTool tests and behavior remain green. +- [ ] Installed `amm-author.exe` runs from outside the checkout. +- [ ] No fork, push, pull request, release, or redistribution occurred. From 5158d75600038a0fdd4f5c6a2b480a7b545e984c Mon Sep 17 00:00:00 2001 From: buu420 <43735748+buu420@users.noreply.github.com> Date: Tue, 4 Aug 2026 20:15:34 -0500 Subject: [PATCH 03/12] refactor: share AuthorTool services --- AccessibilityModManager.slnx | 1 + .../AccessibilityModManager.AuthorTool.csproj | 2 +- .../AccessibilityModManager.Authoring.csproj | 20 ++++++++++++++++++ .../Services/AuthorConfig.cs | 0 .../Services/AuthorConfigService.cs | 0 .../Services/ClaimSigningKeyStore.cs | 0 .../Services/GitHubIndexPublisher.cs | 0 .../Services/GitHubService.cs | 0 .../Services/GitService.cs | 0 .../Services/IndexFileService.cs | 0 .../Services/IndexProofService.cs | 0 .../Services/IndexPublishCoordinator.cs | 0 .../Services/LocalIndexAdoption.cs | 0 .../Services/ManifestBuilderService.cs | 0 .../Services/PatreonAuthorService.cs | 0 .../Services/ProcessRunner.cs | 0 .../Services/ProjectReconciler.cs | 0 .../Services/PublishLock.cs | 0 .../Services/PublishPresentation.cs | 0 .../Services/PublisherHeadStore.cs | 0 .../Services/RegistryMembershipChecker.cs | 0 .../Services/ServerSelfTest.cs | 0 .../Services/ServerUploadService.cs | 0 .../Services/Sha256HashService.cs | 0 .../Services/UnsignedPublishGate.cs | 0 .../AccessibilityModManager.Tests.csproj | 1 + .../Authoring/AuthoringAssemblyTests.cs | 21 +++++++++++++++++++ 27 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/AccessibilityModManager.Authoring/AccessibilityModManager.Authoring.csproj rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/AuthorConfig.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/AuthorConfigService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/ClaimSigningKeyStore.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/GitHubIndexPublisher.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/GitHubService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/GitService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/IndexFileService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/IndexProofService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/IndexPublishCoordinator.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/LocalIndexAdoption.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/ManifestBuilderService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/PatreonAuthorService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/ProcessRunner.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/ProjectReconciler.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/PublishLock.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/PublishPresentation.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/PublisherHeadStore.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/RegistryMembershipChecker.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/ServerSelfTest.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/ServerUploadService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/Sha256HashService.cs (100%) rename src/{AccessibilityModManager.AuthorTool => AccessibilityModManager.Authoring}/Services/UnsignedPublishGate.cs (100%) create mode 100644 tests/AccessibilityModManager.Tests/Authoring/AuthoringAssemblyTests.cs diff --git a/AccessibilityModManager.slnx b/AccessibilityModManager.slnx index f2cb105..1707a75 100644 --- a/AccessibilityModManager.slnx +++ b/AccessibilityModManager.slnx @@ -1,6 +1,7 @@ + diff --git a/src/AccessibilityModManager.AuthorTool/AccessibilityModManager.AuthorTool.csproj b/src/AccessibilityModManager.AuthorTool/AccessibilityModManager.AuthorTool.csproj index 0bea13a..eb0cc28 100644 --- a/src/AccessibilityModManager.AuthorTool/AccessibilityModManager.AuthorTool.csproj +++ b/src/AccessibilityModManager.AuthorTool/AccessibilityModManager.AuthorTool.csproj @@ -1,6 +1,7 @@ + @@ -10,7 +11,6 @@ - diff --git a/src/AccessibilityModManager.Authoring/AccessibilityModManager.Authoring.csproj b/src/AccessibilityModManager.Authoring/AccessibilityModManager.Authoring.csproj new file mode 100644 index 0000000..33f3040 --- /dev/null +++ b/src/AccessibilityModManager.Authoring/AccessibilityModManager.Authoring.csproj @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + net10.0-windows + enable + enable + AccessibilityModManager.Authoring + + + diff --git a/src/AccessibilityModManager.AuthorTool/Services/AuthorConfig.cs b/src/AccessibilityModManager.Authoring/Services/AuthorConfig.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/AuthorConfig.cs rename to src/AccessibilityModManager.Authoring/Services/AuthorConfig.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/AuthorConfigService.cs b/src/AccessibilityModManager.Authoring/Services/AuthorConfigService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/AuthorConfigService.cs rename to src/AccessibilityModManager.Authoring/Services/AuthorConfigService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/ClaimSigningKeyStore.cs b/src/AccessibilityModManager.Authoring/Services/ClaimSigningKeyStore.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/ClaimSigningKeyStore.cs rename to src/AccessibilityModManager.Authoring/Services/ClaimSigningKeyStore.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/GitHubIndexPublisher.cs b/src/AccessibilityModManager.Authoring/Services/GitHubIndexPublisher.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/GitHubIndexPublisher.cs rename to src/AccessibilityModManager.Authoring/Services/GitHubIndexPublisher.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/GitHubService.cs b/src/AccessibilityModManager.Authoring/Services/GitHubService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/GitHubService.cs rename to src/AccessibilityModManager.Authoring/Services/GitHubService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/GitService.cs b/src/AccessibilityModManager.Authoring/Services/GitService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/GitService.cs rename to src/AccessibilityModManager.Authoring/Services/GitService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/IndexFileService.cs b/src/AccessibilityModManager.Authoring/Services/IndexFileService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/IndexFileService.cs rename to src/AccessibilityModManager.Authoring/Services/IndexFileService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/IndexProofService.cs b/src/AccessibilityModManager.Authoring/Services/IndexProofService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/IndexProofService.cs rename to src/AccessibilityModManager.Authoring/Services/IndexProofService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/IndexPublishCoordinator.cs b/src/AccessibilityModManager.Authoring/Services/IndexPublishCoordinator.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/IndexPublishCoordinator.cs rename to src/AccessibilityModManager.Authoring/Services/IndexPublishCoordinator.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/LocalIndexAdoption.cs b/src/AccessibilityModManager.Authoring/Services/LocalIndexAdoption.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/LocalIndexAdoption.cs rename to src/AccessibilityModManager.Authoring/Services/LocalIndexAdoption.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/ManifestBuilderService.cs b/src/AccessibilityModManager.Authoring/Services/ManifestBuilderService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/ManifestBuilderService.cs rename to src/AccessibilityModManager.Authoring/Services/ManifestBuilderService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/PatreonAuthorService.cs b/src/AccessibilityModManager.Authoring/Services/PatreonAuthorService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/PatreonAuthorService.cs rename to src/AccessibilityModManager.Authoring/Services/PatreonAuthorService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/ProcessRunner.cs b/src/AccessibilityModManager.Authoring/Services/ProcessRunner.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/ProcessRunner.cs rename to src/AccessibilityModManager.Authoring/Services/ProcessRunner.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/ProjectReconciler.cs b/src/AccessibilityModManager.Authoring/Services/ProjectReconciler.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/ProjectReconciler.cs rename to src/AccessibilityModManager.Authoring/Services/ProjectReconciler.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/PublishLock.cs b/src/AccessibilityModManager.Authoring/Services/PublishLock.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/PublishLock.cs rename to src/AccessibilityModManager.Authoring/Services/PublishLock.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/PublishPresentation.cs b/src/AccessibilityModManager.Authoring/Services/PublishPresentation.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/PublishPresentation.cs rename to src/AccessibilityModManager.Authoring/Services/PublishPresentation.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/PublisherHeadStore.cs b/src/AccessibilityModManager.Authoring/Services/PublisherHeadStore.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/PublisherHeadStore.cs rename to src/AccessibilityModManager.Authoring/Services/PublisherHeadStore.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/RegistryMembershipChecker.cs b/src/AccessibilityModManager.Authoring/Services/RegistryMembershipChecker.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/RegistryMembershipChecker.cs rename to src/AccessibilityModManager.Authoring/Services/RegistryMembershipChecker.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/ServerSelfTest.cs b/src/AccessibilityModManager.Authoring/Services/ServerSelfTest.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/ServerSelfTest.cs rename to src/AccessibilityModManager.Authoring/Services/ServerSelfTest.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/ServerUploadService.cs b/src/AccessibilityModManager.Authoring/Services/ServerUploadService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/ServerUploadService.cs rename to src/AccessibilityModManager.Authoring/Services/ServerUploadService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/Sha256HashService.cs b/src/AccessibilityModManager.Authoring/Services/Sha256HashService.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/Sha256HashService.cs rename to src/AccessibilityModManager.Authoring/Services/Sha256HashService.cs diff --git a/src/AccessibilityModManager.AuthorTool/Services/UnsignedPublishGate.cs b/src/AccessibilityModManager.Authoring/Services/UnsignedPublishGate.cs similarity index 100% rename from src/AccessibilityModManager.AuthorTool/Services/UnsignedPublishGate.cs rename to src/AccessibilityModManager.Authoring/Services/UnsignedPublishGate.cs diff --git a/tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj b/tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj index 45d1687..061f2fd 100644 --- a/tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj +++ b/tests/AccessibilityModManager.Tests/AccessibilityModManager.Tests.csproj @@ -16,6 +16,7 @@ +