Full Code of ScottArbeit/Grace for AI

main d93b00e5ef04 cached
389 files
4.6 MB
1.2M tokens
17 symbols
1 requests
Download .txt
Showing preview only (4,888K chars total). Download the full file or copy to clipboard to get everything.
Repository: ScottArbeit/Grace
Branch: main
Commit: d93b00e5ef04
Files: 389
Total size: 4.6 MB

Directory structure:
gitextract_t_kevelm/

├── .beads/
│   ├── .gitignore
│   ├── README.md
│   ├── config.yaml
│   ├── interactions.jsonl
│   ├── issues.jsonl
│   └── metadata.json
├── .config/
│   └── dotnet-tools.json
├── .gitattributes
├── .github/
│   ├── code_review_instructions.md
│   ├── copilot-instructions.md
│   └── workflows/
│       ├── dotnet.yml
│       └── validate.yml
├── .gitignore
├── .markdownlint.jsonc
├── AGENTS.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── REPO_INDEX.md
├── SECURITY.md
├── _endpoint_stub.txt
├── code_of_conduct.md
├── docs/
│   ├── .markdownlint.jsonc
│   ├── AI Submissions.md
│   ├── Authentication.md
│   ├── Branching strategy.md
│   ├── Continuous review.md
│   ├── Data types in Grace.md
│   ├── Design and Motivations.md
│   ├── Design concepts/
│   │   ├── Backups.md
│   │   └── Directory and file-level ACL's.md
│   ├── Frequently asked questions.md
│   ├── How Grace computes the SHA-256 value.md
│   ├── Mermaid diagrams.md
│   ├── The potential for misusing Grace.md
│   ├── What grace watch does.md
│   ├── Why Auto-Rebase isn't a problem.md
│   ├── Why Grace isn't just a version control system.md
│   └── Work items.md
├── global.json
├── prompts/
│   ├── ContentPack.prompt.md
│   ├── Grace issue summary.md
│   ├── Grace pull request summary.md
│   └── PlanPack.prompt.md
├── scripts/
│   ├── bootstrap.ps1
│   ├── collect-runtime-metadata.ps1
│   ├── dev-local.ps1
│   ├── install-githooks.ps1
│   ├── start-debuglocal.ps1
│   └── validate.ps1
├── src/
│   ├── .aspire/
│   │   └── settings.json
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .gitattributes
│   ├── .github/
│   │   ├── copilot-instructions.md
│   │   └── workflows/
│   │       └── deploy-to-app-service.yml
│   ├── AGENTS.md
│   ├── CountLines.ps1
│   ├── Create-Grace-Objects.ps1
│   ├── Directory.Build.props
│   ├── Grace.Actors/
│   │   ├── AGENTS.md
│   │   ├── AccessControl.Actor.fs
│   │   ├── ActorProxy.Extensions.Actor.fs
│   │   ├── Artifact.Actor.fs
│   │   ├── Branch.Actor.fs
│   │   ├── BranchName.Actor.fs
│   │   ├── CodeGenAttribute.Actor.fs
│   │   ├── Constants.Actor.fs
│   │   ├── Context.Actor.fs
│   │   ├── Diff.Actor.fs
│   │   ├── DirectoryAppearance.Actor.fs
│   │   ├── DirectoryVersion.Actor.fs
│   │   ├── Extensions/
│   │   │   └── MemoryCache.Extensions.Actor.fs
│   │   ├── FileAppearance.Actor.fs
│   │   ├── GlobalLock.Actor.fs
│   │   ├── Grace.Actors.fsproj
│   │   ├── GrainRepository.Actor.fs
│   │   ├── Interfaces.Actor.fs
│   │   ├── NamedSection.Actor.fs
│   │   ├── Organization.Actor.fs
│   │   ├── OrganizationName.Actor.fs
│   │   ├── Owner.Actor.fs
│   │   ├── OwnerName.Actor.fs
│   │   ├── PersonalAccessToken.Actor.fs
│   │   ├── Policy.Actor.fs
│   │   ├── PromotionQueue.Actor.fs
│   │   ├── PromotionSet.Actor.fs
│   │   ├── Reference.Actor.fs
│   │   ├── Reminder.Actor.fs
│   │   ├── Repository.Actor.fs
│   │   ├── Repository.Actor.fs (ApplyEvent Method)
│   │   ├── RepositoryName.Actor.fs
│   │   ├── RepositoryPermission.Actor.fs
│   │   ├── Review.Actor.fs
│   │   ├── Services.Actor.fs
│   │   ├── Timing.Actor.fs
│   │   ├── Types.Actor.fs
│   │   ├── User.Actor.fs
│   │   ├── ValidationResult.Actor.fs
│   │   ├── ValidationSet.Actor.fs
│   │   ├── WorkItem.Actor.fs
│   │   ├── WorkItemNumber.Actor.fs
│   │   └── WorkItemNumberCounter.Actor.fs
│   ├── Grace.Aspire.AppHost/
│   │   ├── AGENTS.md
│   │   ├── Grace.Aspire.AppHost.csproj
│   │   ├── Program.Aspire.AppHost.cs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   └── appsettings.json
│   ├── Grace.Aspire.ServiceDefaults/
│   │   ├── Extensions.cs
│   │   └── Grace.Aspire.ServiceDefaults.csproj
│   ├── Grace.Authorization.Tests/
│   │   ├── AuthorizationSemantics.Tests.fs
│   │   ├── ClaimMapping.Tests.fs
│   │   ├── EndpointAuthorizationManifest.Tests.fs
│   │   ├── Grace.Authorization.Tests.fsproj
│   │   ├── PathPermissions.Tests.fs
│   │   ├── PermissionEvaluator.Tests.fs
│   │   ├── PersonalAccessToken.Tests.fs
│   │   └── Program.fs
│   ├── Grace.CLI/
│   │   ├── AGENTS.md
│   │   ├── Command/
│   │   │   ├── Access.CLI.fs
│   │   │   ├── Admin.CLI.fs
│   │   │   ├── Agent.CLI.fs
│   │   │   ├── Auth.CLI.fs
│   │   │   ├── Branch.CLI.fs
│   │   │   ├── Candidate.CLI.fs
│   │   │   ├── Common.CLI.fs
│   │   │   ├── Config.CLI.fs
│   │   │   ├── Connect.CLI.fs
│   │   │   ├── Diff.CLI.fs
│   │   │   ├── DirectoryVersion.CLI.fs
│   │   │   ├── History.CLI.fs
│   │   │   ├── Maintenance.CLI.fs
│   │   │   ├── Organization.CLI.fs
│   │   │   ├── Owner.CLI.fs
│   │   │   ├── PromotionSet.CLI.fs
│   │   │   ├── Queue.CLI.fs
│   │   │   ├── Reference.CLI.fs
│   │   │   ├── Repository.CLI.fs
│   │   │   ├── Review.CLI.fs
│   │   │   ├── Services.CLI.fs
│   │   │   ├── Watch.CLI.fs
│   │   │   └── WorkItem.CLI.fs
│   │   ├── Conversion.md
│   │   ├── Grace.CLI.fsproj
│   │   ├── HistoryStorage.CLI.fs
│   │   ├── LocalStateDb.CLI.fs
│   │   ├── Log.CLI.fs
│   │   ├── Program.CLI.fs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   ├── Text.CLI.fs
│   │   └── packages-microsoft-prod.deb
│   ├── Grace.CLI.LocalStateDb.Worker/
│   │   ├── Grace.CLI.LocalStateDb.Worker.fsproj
│   │   └── Program.fs
│   ├── Grace.CLI.Tests/
│   │   ├── AGENTS.md
│   │   ├── Agent.CLI.Tests.fs
│   │   ├── Auth.Tests.fs
│   │   ├── AuthTokenBundle.Tests.fs
│   │   ├── Connect.CLI.Tests.fs
│   │   ├── Grace.CLI.Tests.fsproj
│   │   ├── History.CLI.Tests.fs
│   │   ├── HistoryStorage.CLI.Tests.fs
│   │   ├── LocalStateDb.Tests.fs
│   │   ├── Program.CLI.Tests.fs
│   │   ├── Program.fs
│   │   ├── PromotionSet.CLI.Tests.fs
│   │   ├── Queue.CLI.Tests.fs
│   │   ├── Review.CLI.Tests.fs
│   │   ├── Watch.Tests.fs
│   │   └── WorkItem.CLI.Tests.fs
│   ├── Grace.Load/
│   │   ├── Grace.Load.fsproj
│   │   ├── Program.Load.fs
│   │   └── Properties/
│   │       └── launchSettings.json
│   ├── Grace.Orleans.CodeGen/
│   │   ├── Declaration.Orleans.CodeGen.cs
│   │   ├── Grace.Orleans.CodeGen.csproj
│   │   └── instructions.md
│   ├── Grace.SDK/
│   │   ├── AGENTS.md
│   │   ├── Access.SDK.fs
│   │   ├── Admin.SDK.fs
│   │   ├── Artifact.SDK.fs
│   │   ├── Auth.SDK.fs
│   │   ├── Branch.SDK.fs
│   │   ├── Common.SDK.fs
│   │   ├── Diff.SDK.fs
│   │   ├── DirectoryVersion.SDK.fs
│   │   ├── Grace.SDK.fsproj
│   │   ├── Organization.SDK.fs
│   │   ├── Owner.SDK.fs
│   │   ├── PersonalAccessToken.SDK.fs
│   │   ├── Policy.SDK.fs
│   │   ├── PromotionSet.SDK.fs
│   │   ├── Queue.SDK.fs
│   │   ├── Repository.SDK.fs
│   │   ├── Review.SDK.fs
│   │   ├── Storage.SDK.fs
│   │   ├── ValidationResult.SDK.fs
│   │   ├── ValidationSet.SDK.fs
│   │   └── WorkItem.SDK.fs
│   ├── Grace.Server/
│   │   ├── AGENTS.md
│   │   ├── Access.Server.fs
│   │   ├── ApplicationContext.Server.fs
│   │   ├── Artifact.Server.fs
│   │   ├── Auth.Server.fs
│   │   ├── Branch.Server.fs
│   │   ├── CorrelationId.Server.fs
│   │   ├── DerivedComputation.Server.fs
│   │   ├── Diff.Server.fs
│   │   ├── DirectoryVersion.Server.fs
│   │   ├── Dockerfile
│   │   ├── Eventing.Server.fs
│   │   ├── Grace.Server.fsproj
│   │   ├── Middleware/
│   │   │   ├── CorrelationId.Middleware.fs
│   │   │   ├── Fake.Middleware.fs
│   │   │   ├── HttpSecurityHeaders.Middleware.fs
│   │   │   ├── LogAuthorizationFailure.Middleware.fs
│   │   │   ├── LogRequestHeaders.Middleware.fs
│   │   │   ├── Timing.Middleware.fs
│   │   │   └── ValidateIds.Middleware.fs
│   │   ├── Notification.Server.fs
│   │   ├── Organization.Server.fs
│   │   ├── OrleansFilters.Server.fs
│   │   ├── Owner.Server.fs
│   │   ├── PartitionKeyProvider.fs
│   │   ├── Policy.Server.fs
│   │   ├── Program.Server.fs
│   │   ├── PromotionSet.Server.fs
│   │   ├── Properties/
│   │   │   ├── PublishProfiles/
│   │   │   │   └── DisableContainerBuild.pubxml
│   │   │   └── launchSettings.json
│   │   ├── Queue.Server.fs
│   │   ├── Reminder.Server.fs
│   │   ├── ReminderService.Server.fs
│   │   ├── Repository.Server.fs
│   │   ├── Review.Server.fs
│   │   ├── ReviewAnalysis.Server.fs
│   │   ├── ReviewModels.Server.fs
│   │   ├── Security/
│   │   │   ├── AuthorizationMiddleware.Server.fs
│   │   │   ├── ClaimMapping.Server.fs
│   │   │   ├── ClaimsTransformation.Server.fs
│   │   │   ├── EndpointAuthorizationManifest.Server.fs
│   │   │   ├── ExternalAuthConfig.Server.fs
│   │   │   ├── PermissionEvaluator.Server.fs
│   │   │   ├── PersonalAccessTokenAuth.Server.fs
│   │   │   ├── PrincipalMapper.Server.fs
│   │   │   └── TestAuth.Server.fs
│   │   ├── Services.Server.fs
│   │   ├── Startup.Server.fs
│   │   ├── Storage.Server.fs
│   │   ├── ValidationResult.Server.fs
│   │   ├── ValidationSet.Server.fs
│   │   ├── Validations.Server.fs
│   │   ├── WorkItem.Server.fs
│   │   ├── appsettings.json
│   │   └── web.config
│   ├── Grace.Server.Tests/
│   │   ├── AGENTS.md
│   │   ├── Access.Server.Tests.fs
│   │   ├── AspireTestHost.fs
│   │   ├── Auth.Server.Tests.fs
│   │   ├── AuthMapping.Unit.Tests.fs
│   │   ├── Authorization.Unit.Tests.fs
│   │   ├── Eventing.Server.Tests.fs
│   │   ├── Evidence.Determinism.Tests.fs
│   │   ├── General.Server.Tests.fs
│   │   ├── Grace.Server.Tests.fsproj
│   │   ├── Notification.Server.Tests.fs
│   │   ├── OrleansFilters.Server.Tests.fs
│   │   ├── Owner.Server.Tests.fs
│   │   ├── Policy.Determinism.Tests.fs
│   │   ├── Policy.Validation.Derived.Tests.fs
│   │   ├── Program.fs
│   │   ├── PromotionSet.CommandValidation.Tests.fs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   ├── Queue.Server.Tests.fs
│   │   ├── Repository.Server.Tests.fs
│   │   ├── Review.Server.Tests.fs
│   │   ├── ReviewNotes.Determinism.Tests.fs
│   │   ├── Services.EffectivePromotion.Tests.fs
│   │   ├── Slop.Server.Tests.fs
│   │   ├── Smoke.Server.Tests.fs
│   │   ├── Validation.Artifact.Contract.Tests.fs
│   │   ├── Validations.Server.Tests.fs
│   │   ├── WorkItem.Integration.Server.Tests.fs
│   │   ├── WorkItem.Server.Tests.fs
│   │   └── appsettings.json
│   ├── Grace.Shared/
│   │   ├── AGENTS.md
│   │   ├── Authorization.Shared.fs
│   │   ├── AzureEnvironment.Shared.fs
│   │   ├── BaselineDrift.Shared.fs
│   │   ├── Client/
│   │   │   ├── Configuration.Shared.fs
│   │   │   ├── Theme.Shared.fs
│   │   │   └── UserConfiguration.Shared.fs
│   │   ├── Combinators.fs
│   │   ├── Constants.Shared.fs
│   │   ├── Converters/
│   │   │   └── BranchDtoConverter.Shared.fs
│   │   ├── Diff.Shared.fs
│   │   ├── Dto/
│   │   │   └── Dto.Shared.fs
│   │   ├── Evidence.Shared.fs
│   │   ├── Extensions.Shared.fs
│   │   ├── Grace.Shared.fsproj
│   │   ├── Monikers.imagemanifest
│   │   ├── Parameters/
│   │   │   ├── Access.Parameters.fs
│   │   │   ├── Artifact.Parameters.fs
│   │   │   ├── Auth.Parameters.fs
│   │   │   ├── Branch.Parameters.fs
│   │   │   ├── Common.Parameters.fs
│   │   │   ├── Diff.Parameters.fs
│   │   │   ├── Directory.Parameters.fs
│   │   │   ├── Organization.Parameters.fs
│   │   │   ├── Owner.Parameters.fs
│   │   │   ├── Policy.Parameters.fs
│   │   │   ├── PromotionSet.Parameters.fs
│   │   │   ├── Queue.Parameters.fs
│   │   │   ├── Reference.Parameters.fs
│   │   │   ├── Reminder.Parameters.fs
│   │   │   ├── Repository.Parameters.fs
│   │   │   ├── Review.Parameters.fs
│   │   │   ├── Storage.Parameters.fs
│   │   │   ├── Validation.Parameters.fs
│   │   │   └── WorkItem.Parameters.fs
│   │   ├── Resources/
│   │   │   ├── Text/
│   │   │   │   ├── Languages.Resources.fs
│   │   │   │   └── en-US.fs
│   │   │   └── Utilities.Resources.fs
│   │   ├── ReviewNotes.Shared.fs
│   │   ├── Services.Shared.fs
│   │   ├── Utilities.Shared.fs
│   │   └── Validation/
│   │       ├── Common.Validation.fs
│   │       ├── Connect.Validation.fs
│   │       ├── Errors.Validation.fs
│   │       ├── Repository.Validation.fs
│   │       └── Utilities.Validation.fs
│   ├── Grace.Types/
│   │   ├── AGENTS.md
│   │   ├── Artifact.Types.fs
│   │   ├── Auth.Types.fs
│   │   ├── Authorization.Types.fs
│   │   ├── Automation.Types.fs
│   │   ├── Branch.Types.fs
│   │   ├── Diff.Types.fs
│   │   ├── DirectoryVersion.Types.fs
│   │   ├── Events.Types.fs
│   │   ├── Grace.Types.fsproj
│   │   ├── Organization.Types.fs
│   │   ├── Owner.Types.fs
│   │   ├── PersonalAccessToken.Types.fs
│   │   ├── Policy.Types.fs
│   │   ├── PromotionSet.Types.fs
│   │   ├── Queue.Types.fs
│   │   ├── Reference.Types.fs
│   │   ├── Reminder.Types.fs
│   │   ├── Repository.Types.fs
│   │   ├── RequiredAction.Types.fs
│   │   ├── Review.Types.fs
│   │   ├── Types.Types.fs
│   │   ├── Validation.Types.fs
│   │   └── WorkItem.Types.fs
│   ├── Grace.Types.Tests/
│   │   ├── AGENTS.md
│   │   ├── Automation.Types.Tests.fs
│   │   ├── Grace.Types.Tests.fsproj
│   │   ├── Program.fs
│   │   ├── PromotionSet.ConflictModel.Types.Tests.fs
│   │   ├── PromotionSet.Types.Tests.fs
│   │   ├── Queue.Types.Tests.fs
│   │   ├── Validation.Types.Tests.fs
│   │   └── WorkItem.Types.Tests.fs
│   ├── Grace.slnx
│   ├── OpenAPI/
│   │   ├── Branch.Components.OpenAPI.yaml
│   │   ├── Branch.Paths.OpenAPI.yaml
│   │   ├── Diff.Components.OpenAPI.yaml
│   │   ├── Diff.Paths.OpenAPI.yaml
│   │   ├── Directory.Components.OpenAPI.yaml
│   │   ├── Directory.Paths.OpenAPI.yaml
│   │   ├── Dto.Components.OpenAPI.yaml
│   │   ├── Grace.OpenAPI.yaml
│   │   ├── Main.OpenAPI.yaml
│   │   ├── Organization.Components.OpenAPI.yaml
│   │   ├── Organization.Paths.OpenAPI.yaml
│   │   ├── Owner.Components.OpenAPI.yaml
│   │   ├── Owner.Paths.OpenAPI.yaml
│   │   ├── Reference.Components.OpenAPI.yaml
│   │   ├── Repository.Components.OpenAPI.yaml
│   │   ├── Repository.Paths.OpenAPI.yaml
│   │   ├── Responses.OpenAPI.yaml
│   │   ├── Shared.Components.OpenAPI.yaml
│   │   └── Shared2.Components.OpenAPI.yaml
│   ├── Processing file system changes.md
│   ├── docs/
│   │   ├── ASPIRE_SETUP.md
│   │   └── ENVIRONMENT.md
│   ├── fantomas-config.json
│   ├── launchSettings.json
│   ├── nuget.config
│   └── packages-microsoft-prod.deb
└── update-contributing.skill

================================================
FILE CONTENTS
================================================

================================================
FILE: .beads/.gitignore
================================================
# SQLite databases
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm

# Daemon runtime files
daemon.lock
daemon.log
daemon.pid
bd.sock
sync-state.json
last-touched

# Local version tracking (prevents upgrade notification spam after git ops)
.local_version

# Legacy database files
db.sqlite
bd.db

# Worktree redirect file (contains relative path to main repo's .beads/)
# Must not be committed as paths would be wrong in other clones
redirect

# Merge artifacts (temporary files from 3-way merge)
beads.base.jsonl
beads.base.meta.json
beads.left.jsonl
beads.left.meta.json
beads.right.jsonl
beads.right.meta.json

# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here.
# They would override fork protection in .git/info/exclude, allowing
# contributors to accidentally commit upstream issue databases.
# The JSONL files (issues.jsonl, interactions.jsonl) and config files
# are tracked by git by default since no pattern above ignores them.


================================================
FILE: .beads/README.md
================================================
# Beads - AI-Native Issue Tracking

Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.

## What is Beads?

Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.

**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)

## Quick Start

### Essential Commands

```bash
# Create new issues
bd create "Add user authentication"

# View all issues
bd list

# View issue details
bd show <issue-id>

# Update issue status
bd update <issue-id> --status in_progress
bd update <issue-id> --status done

# Sync with git remote
bd sync
```

### Working with Issues

Issues in Beads are:
- **Git-native**: Stored in `.beads/issues.jsonl` and synced like code
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
- **Branch-aware**: Issues can follow your branch workflow
- **Always in sync**: Auto-syncs with your commits

## Why Beads?

✨ **AI-Native Design**
- Built specifically for AI-assisted development workflows
- CLI-first interface works seamlessly with AI coding agents
- No context switching to web UIs

🚀 **Developer Focused**
- Issues live in your repo, right next to your code
- Works offline, syncs when you push
- Fast, lightweight, and stays out of your way

🔧 **Git Integration**
- Automatic sync with git commits
- Branch-aware issue tracking
- Intelligent JSONL merge resolution

## Get Started with Beads

Try Beads in your own projects:

```bash
# Install Beads
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash

# Initialize in your repo
bd init

# Create your first issue
bd create "Try out Beads"
```

## Learn More

- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
- **Quick Start Guide**: Run `bd quickstart`
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)

---

*Beads: Issue tracking that moves at the speed of thought* ⚡


================================================
FILE: .beads/config.yaml
================================================
# Beads Configuration File
# This file configures default behavior for all bd commands in this repository
# All settings can also be set via environment variables (BD_* prefix)
# or overridden with command-line flags

# Issue prefix for this repository (used by bd init)
# If not set, bd init will auto-detect from directory name
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
# issue-prefix: ""

# Use no-db mode: load from JSONL, no SQLite, write back after each command
# When true, bd will use .beads/issues.jsonl as the source of truth
# instead of SQLite database
# no-db: false

# Disable daemon for RPC communication (forces direct database access)
# no-daemon: false

# Disable auto-flush of database to JSONL after mutations
# no-auto-flush: false

# Disable auto-import from JSONL when it's newer than database
# no-auto-import: false

# Enable JSON output by default
# json: false

# Default actor for audit trails (overridden by BD_ACTOR or --actor)
# actor: ""

# Path to database (overridden by BEADS_DB or --db)
# db: ""

# Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON)
# auto-start-daemon: true

# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE)
# flush-debounce: "5s"

# Git branch for beads commits (bd sync will commit to this branch)
# IMPORTANT: Set this for team projects so all clones use the same sync branch.
# This setting persists across clones (unlike database config which is gitignored).
# Can also use BEADS_SYNC_BRANCH env var for local override.
# If not set, bd sync will require you to run 'bd config set sync.branch <branch>'.
# sync-branch: "beads-sync"

# Multi-repo configuration (experimental - bd-307)
# Allows hydrating from multiple repositories and routing writes to the correct JSONL
# repos:
#   primary: "."  # Primary repo (where this database lives)
#   additional:   # Additional repos to hydrate from (read-only)
#     - ~/beads-planning  # Personal planning repo
#     - ~/work-planning   # Work planning repo

# Integration settings (access with 'bd config get/set')
# These are stored in the database, not in this file:
# - jira.url
# - jira.project
# - linear.url
# - linear.api-key
# - github.org
# - github.repo


================================================
FILE: .beads/interactions.jsonl
================================================


================================================
FILE: .beads/issues.jsonl
================================================
{"id":"Grace-14w","title":"P1 Add src/docs/ENVIRONMENT.md from EnvironmentVariables","description":"Document env vars and dependencies (Docker services, auth forwarding) using Grace.Shared.EnvironmentVariables as source of truth.","acceptance_criteria":"ENVIRONMENT.md matches code constants; markdownlint passes.","notes":"Added src/docs/ENVIRONMENT.md documenting Docker dependencies, Aspire run modes, test toggles, auth forwarding, and all EnvironmentVariables entries.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-09T12:31:02.7535804-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:11:35.0366104-08:00","closed_at":"2026-01-09T13:11:35.0366104-08:00","dependencies":[{"issue_id":"Grace-14w","depends_on_id":"Grace-4ri","type":"blocks","created_at":"2026-01-09T12:46:38.9582602-08:00","created_by":"unknown"}]}
{"id":"Grace-19m","title":"Ensure CI runs Fantomas format check","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-01-09T23:05:13.2613072-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T23:12:18.120212-08:00","closed_at":"2026-01-09T23:12:18.120212-08:00","close_reason":"Closed"}
{"id":"Grace-2k6","title":"CLI: Queue commands","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:55.0263408-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T04:52:05.2894182-08:00","closed_at":"2026-01-06T04:52:05.2894182-08:00","close_reason":"Closed"}
{"id":"Grace-3yl","title":"P0 Bootstrap script: restore tools/packages + output","description":"Extend scripts/bootstrap.ps1 to run dotnet tool restore and restore NuGet packages for chosen build/test targets; print next commands and elapsed time on success/failure.","acceptance_criteria":"bootstrap.ps1 performs tool restore + package restore; prints next steps (validate -Fast) and elapsed time; exits 0 on success.","notes":"bootstrap.ps1 restores tools and src/Grace.sln packages, prints next steps and elapsed time. Ran pwsh ./scripts/bootstrap.ps1 -SkipDocker -CI successfully.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:29:04.7741508-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:05:47.2243686-08:00","closed_at":"2026-01-09T13:05:47.2243686-08:00","dependencies":[{"issue_id":"Grace-3yl","depends_on_id":"Grace-ami","type":"blocks","created_at":"2026-01-09T12:32:52.5983134-08:00","created_by":"unknown"},{"issue_id":"Grace-3yl","depends_on_id":"Grace-4d0","type":"blocks","created_at":"2026-01-09T12:32:57.929904-08:00","created_by":"unknown"}]}
{"id":"Grace-40p","title":"P0 CI workflow: validate -Full on main/nightly","description":"Extend validate workflow to run pwsh ./scripts/validate.ps1 -Full on main pushes and/or nightly schedule; ensure Docker available for Aspire tests.","acceptance_criteria":"Full validation runs on main/nightly and fails if full suite fails; logs show stage failures.","notes":"validate.yml runs -Full on main pushes and nightly schedule with docker info step.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:30:30.6850857-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:09:31.9266816-08:00","closed_at":"2026-01-09T13:09:31.9266816-08:00","dependencies":[{"issue_id":"Grace-40p","depends_on_id":"Grace-701","type":"blocks","created_at":"2026-01-09T12:34:07.4312764-08:00","created_by":"unknown"},{"issue_id":"Grace-40p","depends_on_id":"Grace-wv2","type":"blocks","created_at":"2026-01-09T12:34:12.7693199-08:00","created_by":"unknown"}]}
{"id":"Grace-49s","title":"CLI: WorkItem commands","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:54.5109757-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T04:03:39.3374957-08:00","closed_at":"2026-01-06T04:03:39.3374957-08:00","close_reason":"Closed"}
{"id":"Grace-4ba","title":"P0 Define bootstrap/validate semantics + acceptance","description":"Draft exact behavior for scripts (flags, working dir vs explicit paths, format check strategy, build/test targets) and failure/success outputs; include 'I know I'm done when…' checks.","acceptance_criteria":"Issue contains concrete command list for bootstrap/validate, flag behavior, and acceptance checks for success/failure paths.","notes":"Proposed semantics:\\nbootstrap.ps1\\n- StrictMode, Continue='Stop'.\\n- Flags: -SkipDocker, -CI (no prompts), -Verbose (PS standard).\\n- Checks: PowerShell 7.x (PSVersionTable.PSVersion.Major -ge 7), dotnet available (Get-Command dotnet), dotnet SDK resolves to net10 (dotnet --version + optionally dotnet --list-sdks; if global.json missing, warn), Docker running unless -SkipDocker (docker info).\\n- Actions: dotnet tool restore; dotnet restore src/Grace.sln (or explicit projects if needed).\\n- Output: grouped headings (Prereqs/Restore/Next steps), print elapsed time on success/failure. Exit 0 on success, non-zero otherwise.\\n\\nvalidate.ps1\\n- StrictMode, stop-on-error, grouped output: Format, Build, Test. Always prints elapsed time.\\n- Flags: -Fast (default), -Full, -SkipFormat/-SkipBuild/-SkipTests, -Configuration (default Release).\\n- Format stage: dotnet tool restore; run pinned Fantomas against src/ using src/fantomas-config.json. Prefer dotnet tool run fantomas --check src --config src/fantomas-config.json; if --check not reliable, run format then git status --porcelain diff and fail with guidance.\\n- Build stage: dotnet build src/Grace.Server/Grace.Server.fsproj -c \u003cconfig\u003e; dotnet build src/Grace.CLI/Grace.CLI.fsproj -c \u003cconfig\u003e; optional Grace.SDK depending on inventory.\\n- Test stage: -Fast runs dotnet test src/Grace.CLI.Tests/Grace.CLI.Tests.fsproj -c \u003cconfig\u003e --no-build. -Full runs that plus dotnet test src/Grace.Server.Tests/Grace.Server.Tests.fsproj -c \u003cconfig\u003e --no-build (Aspire/Docker).\\n\\nAcceptance checks for scripts:\\n- bootstrap: missing docker =\u003e fail with actionable message unless -SkipDocker; missing dotnet/pwsh =\u003e fail; tool restore + package restore succeed; prints next steps.\\n- validate: any stage failure =\u003e non-zero exit; formatting drift yields actionable guidance; -Fast avoids Docker; -Full exercises Aspire tests.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:28:32.6383989-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:33:02.276854-08:00","closed_at":"2026-01-09T13:33:02.276854-08:00","close_reason":"Closed"}
{"id":"Grace-4d0","title":"P0 Add .config/dotnet-tools.json with pinned Fantomas","description":"Create local tool manifest at repo root and pin fantomas-tool version; ensure validate uses dotnet tool run.","acceptance_criteria":"dotnet tool restore succeeds at repo root; dotnet tool run fantomas --version matches pinned version.","notes":"Added .config/dotnet-tools.json pinning fantomas-tool 4.7.9. dotnet tool restore + dotnet tool run fantomas --version succeeded.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:28:42.9941774-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T12:52:49.5079712-08:00","closed_at":"2026-01-09T12:52:49.5079712-08:00","dependencies":[{"issue_id":"Grace-4d0","depends_on_id":"Grace-w54","type":"blocks","created_at":"2026-01-09T12:32:36.5957243-08:00","created_by":"unknown"}]}
{"id":"Grace-4g4","title":"Fix verbose parse result option handling","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-09T01:34:19.1573701-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T01:38:22.0352754-08:00","closed_at":"2026-01-09T01:38:22.0352754-08:00","close_reason":"Closed"}
{"id":"Grace-4ri","title":"P0 Inventory current build/test/format commands","description":"Read AGENTS.md, src/AGENTS.md, src/docs/ASPIRE_SETUP.md, src/fantomas-config.json, src/.editorconfig; locate sln/projects/tests and existing scripts; identify fast vs full test candidates.","acceptance_criteria":"Notes captured in issue: canonical build targets, fast tests, full tests, format config paths, any existing scripts/tools.","notes":"Inventory summary:\\n- Solution: src/Grace.sln\\n- Projects: Grace.Server, Grace.CLI, Grace.SDK, Grace.Actors, Grace.Shared, Grace.Types, Grace.Load; tests: Grace.CLI.Tests, Grace.Server.Tests\\n- Format config: src/fantomas-config.json; .editorconfig lives under src/\\n- Current guidance: dotnet build --configuration Release; dotnet test --no-build; run fantomas . after F# changes (root + project AGENTS).\\n- Aspire: src/docs/ASPIRE_SETUP.md (Aspire app host in Grace.Aspire.AppHost; tests reuse running emulators)\\n- Grace.Server.Tests uses [\u003cSetUpFixture\u003e] in namespace Grace.Server.Tests and AspireTestHost.startAsync() which spins containers; first call must be POST /owner/create per src/Grace.Server.Tests/AGENTS.md.\\n- Fast tests candidate: src/Grace.CLI.Tests/Grace.CLI.Tests.fsproj (non-Docker). Full tests: src/Grace.Server.Tests/Grace.Server.Tests.fsproj (Aspire).\\n- No scripts/ directory currently in repo root.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:28:20.9873729-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:32:51.8031394-08:00","closed_at":"2026-01-09T13:32:51.8031394-08:00","close_reason":"Closed"}
{"id":"Grace-4x5","title":"Tests: policy parser/snapshot determinism","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:55.2846176-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T05:13:35.5451478-08:00","closed_at":"2026-01-06T05:13:35.5451478-08:00","close_reason":"Closed"}
{"id":"Grace-5c3","title":"Authorization/Access control implementation","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-28T23:07:44.6284068-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-30T02:05:17.4202501-08:00","closed_at":"2025-12-30T02:05:17.4202501-08:00","close_reason":"All tasks completed"}
{"id":"Grace-5c3.1","title":"Authz: codebase reconnaissance and conventions","description":"Review existing patterns (actors, handlers, params, SDK/CLI) and locate stubs/target endpoints to protect; confirm serializer and validation conventions.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-28T23:07:58.2365723-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-28T23:14:19.3631334-08:00","closed_at":"2025-12-28T23:14:19.3631334-08:00","close_reason":"Recon complete","dependencies":[{"issue_id":"Grace-5c3.1","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:07:58.2414666-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.10","title":"Authz: unit + integration tests","description":"Add pure authorization unit tests and server integration tests for /access, enforcement, and auth headers; update General.Server.Tests.fs headers.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-28T23:08:36.7155492-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-29T00:17:49.3242606-08:00","closed_at":"2025-12-29T00:17:49.3242606-08:00","close_reason":"Closed","dependencies":[{"issue_id":"Grace-5c3.10","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:36.7204264-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.11","title":"Authz: formatting/build/test verification","description":"Run fantomas, dotnet build Release, and dotnet test for Server.Tests + CLI.Tests after code changes.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-28T23:08:40.9300256-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-29T01:27:14.0420934-08:00","closed_at":"2025-12-29T01:27:14.0420934-08:00","dependencies":[{"issue_id":"Grace-5c3.11","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:40.9347831-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.12","title":"Auth: external auth config + claim mapping","description":"Add auth provider config model + env var bindings; implement claims transformation mapping external provider claims to grace_user_id and grace_claim (MSA + Entra). Include provider-agnostic mapping helper + /auth/me endpoint + unit tests for mapping logic.","notes":"Config + claim mapping + /auth/me + unit tests","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-30T00:20:35.5829516-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-30T01:08:29.3280438-08:00","closed_at":"2025-12-30T01:08:29.3280438-08:00","dependencies":[{"issue_id":"Grace-5c3.12","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-30T00:20:35.5835408-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.13","title":"Auth: login provider selection page + endpoints","description":"Add /auth/login page listing configured providers; add /auth/login/{provider} challenge endpoint + returnUrl handling; add /auth/logout endpoint; wire routes in Startup.Server.fs; show only providers with valid config.","notes":"Added /auth/login page, provider challenge route, and /auth/logout.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-30T00:20:37.3729975-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-30T01:10:58.7806669-08:00","closed_at":"2025-12-30T01:10:58.7806669-08:00","dependencies":[{"issue_id":"Grace-5c3.13","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-30T00:20:37.3784688-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.14","title":"Auth: Microsoft (MSA) OIDC + JWT bearer integration","description":"Configure Cookie + OpenIdConnect (Microsoft) and JwtBearer schemes for MSA+Entra; keep GraceTest when GRACE_TESTING=1; set default challenge/forbid scheme selection and callbacks; validate JWT audience for Grace API scope.","notes":"Configured auth schemes: cookie + OIDC + JWT with scheme selection; MSA/Entra authority and audience validation.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-30T00:20:39.1621794-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-30T01:13:51.2451983-08:00","closed_at":"2025-12-30T01:13:51.2451983-08:00","dependencies":[{"issue_id":"Grace-5c3.14","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-30T00:20:39.1676651-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.15","title":"Auth: auth pipeline tests","description":"Add unit tests for claim mapping; integration test for /auth/login provider list + challenge route (no external IdP calls); verify GraceTest auth still works. Add minimal CLI auth tests if feasible without IdP calls.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-30T00:20:41.0655512-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-30T02:02:09.4835738-08:00","closed_at":"2025-12-30T02:02:09.4835738-08:00","dependencies":[{"issue_id":"Grace-5c3.15","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-30T00:20:41.0709904-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.16","title":"Docs: MSA app registration + local config","description":"Document MSA+Entra app registration (web + CLI apps), API scope, redirect URIs, required env vars/user-secrets, and CLI login usage. Note GitHub/Google placeholders for future providers.","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-30T00:20:42.9343128-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-30T02:03:24.8795061-08:00","closed_at":"2025-12-30T02:03:24.8795061-08:00","dependencies":[{"issue_id":"Grace-5c3.16","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-30T00:20:42.9402113-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.17","title":"Auth: CLI/GUI device-code login + token cache","description":"Add CLI auth commands (login/status/logout/whoami) using MSAL device code; store tokens in OS-protected cache; update Grace SDK/CLI HTTP client to send Bearer tokens; update grace connect to require auth / prompt login; support re-auth in existing repos.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-30T01:00:58.2662025-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-30T01:52:08.0842766-08:00","closed_at":"2025-12-30T01:52:08.0842766-08:00","dependencies":[{"issue_id":"Grace-5c3.17","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-30T01:00:58.2777028-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.2","title":"Authz: add authorization domain types","description":"Add Authorization.Types.fs in Grace.Types with Principal/Scope/Resource/Operation/Role types + update fsproj; follow serializer/versioning conventions.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-28T23:08:02.281657-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-28T23:16:34.3723615-08:00","closed_at":"2025-12-28T23:16:34.3723615-08:00","close_reason":"Authorization types added","dependencies":[{"issue_id":"Grace-5c3.2","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:02.2871518-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.3","title":"Authz: implement pure authorization core","description":"Add Authorization.Shared.fs in Grace.Shared with RoleCatalog, scope resolution, effective ops, path permission checks, and checkPermission.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-28T23:08:06.5102728-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-28T23:27:54.83891-08:00","closed_at":"2025-12-28T23:27:54.83891-08:00","close_reason":"Authorization core added","dependencies":[{"issue_id":"Grace-5c3.3","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:06.5158736-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.4","title":"Authz: AccessControl actor + RepositoryPermission actor","description":"Implement AccessControl.Actor.fs + interface + constants; complete RepositoryPermission.Actor.fs with upsert/remove/list path permissions.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-28T23:08:10.4735628-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-28T23:35:20.2381363-08:00","closed_at":"2025-12-28T23:35:20.2381363-08:00","close_reason":"AccessControl + RepositoryPermission actors added","dependencies":[{"issue_id":"Grace-5c3.4","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:10.4792076-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.5","title":"Authz: server security plumbing","description":"Add GraceTest auth handler, PrincipalMapper, and PermissionEvaluator service; wire DI/auth scheme selection in Startup.Server.fs.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-28T23:08:14.5953805-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-28T23:38:47.1199966-08:00","closed_at":"2025-12-28T23:38:47.1199966-08:00","close_reason":"Test auth + permission evaluator wired","dependencies":[{"issue_id":"Grace-5c3.5","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:14.6007802-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.6","title":"Authz: /access parameters, handlers, routing","description":"Add Access.Parameters.fs, Access.Server.fs handlers, and /access routes with metadata/auth requirements.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-28T23:08:19.0690592-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-28T23:45:37.0740752-08:00","closed_at":"2025-12-28T23:45:37.0740752-08:00","close_reason":"Access parameters and endpoints added","dependencies":[{"issue_id":"Grace-5c3.6","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:19.0750897-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.7","title":"Authz: SDK access surface","description":"Add Access.SDK.fs methods for /access endpoints and update Grace.SDK.fsproj.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-28T23:08:23.1901245-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-28T23:46:44.3452613-08:00","closed_at":"2025-12-28T23:46:44.3452613-08:00","close_reason":"Access SDK added","dependencies":[{"issue_id":"Grace-5c3.7","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:23.1949443-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.8","title":"Authz: CLI access commands","description":"Add Access.CLI.fs command tree + options and register in Program.CLI.fs; output supports --json.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-28T23:08:27.2244712-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-29T00:04:34.4116541-08:00","closed_at":"2025-12-29T00:04:34.4116541-08:00","close_reason":"Closed","dependencies":[{"issue_id":"Grace-5c3.8","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:27.2298486-08:00","created_by":"daemon"}]}
{"id":"Grace-5c3.9","title":"Authz: enforce permissions on endpoints","description":"Add requiresPermission middleware and protect at least repo-level + path-level endpoints (e.g., setDescription + getUploadMetadataForFiles).","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-28T23:08:32.0246786-08:00","created_by":"Scott Arbeit","updated_at":"2025-12-29T00:11:24.16764-08:00","closed_at":"2025-12-29T00:11:24.16764-08:00","close_reason":"Closed","dependencies":[{"issue_id":"Grace-5c3.9","depends_on_id":"Grace-5c3","type":"parent-child","created_at":"2025-12-28T23:08:32.0309851-08:00","created_by":"daemon"}]}
{"id":"Grace-6ev","title":"P0 Update root AGENTS.md with Agent Quickstart","description":"Add Agent Quickstart (Local) section with bootstrap/validate commands and links to src/AGENTS.md, src/docs/ASPIRE_SETUP.md, src/docs/ENVIRONMENT.md.","acceptance_criteria":"Root AGENTS.md enables a new contributor to reach validate -Fast without other docs; markdownlint passes.","notes":"Added Agent Quickstart (Local) section with bootstrap/validate commands and links. Markdownlint clean.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:30:09.9470165-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:19:19.5266104-08:00","closed_at":"2026-01-09T13:19:19.5266104-08:00","dependencies":[{"issue_id":"Grace-6ev","depends_on_id":"Grace-3yl","type":"blocks","created_at":"2026-01-09T12:33:40.6102889-08:00","created_by":"unknown"},{"issue_id":"Grace-6ev","depends_on_id":"Grace-oos","type":"blocks","created_at":"2026-01-09T12:33:46.0467868-08:00","created_by":"unknown"}]}
{"id":"Grace-6r8","title":"Server: Gate endpoints","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:50.5517025-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:17:22.7194502-08:00","closed_at":"2026-01-06T02:17:22.7194502-08:00","close_reason":"Closed"}
{"id":"Grace-701","title":"P0 Validate script: full tests (Aspire)","description":"Implement -Full test stage to run Grace.Server.Tests (Aspire.Hosting.Testing) including Docker-required integration tests.","acceptance_criteria":"validate -Full runs full suite including Grace.Server.Tests and fails if Aspire stack fails to start; stage output grouped under Test.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:29:59.7615959-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:33:14.0639429-08:00","closed_at":"2026-01-09T13:33:14.0639429-08:00","close_reason":"Closed","dependencies":[{"issue_id":"Grace-701","depends_on_id":"Grace-sae","type":"blocks","created_at":"2026-01-09T12:33:35.2800434-08:00","created_by":"unknown"}]}
{"id":"Grace-783","title":"Eventing: event envelopes + publishers","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:53.3113195-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:53:25.0597006-08:00","closed_at":"2026-01-06T02:53:25.0597006-08:00","close_reason":"Closed"}
{"id":"Grace-79p","title":"Models: OpenRouter config + wiring","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:51.9340196-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:33:34.4239983-08:00","closed_at":"2026-01-06T02:33:34.4239983-08:00","close_reason":"Closed"}
{"id":"Grace-7it","title":"Continuous Review + Refinery Epic","status":"closed","priority":2,"issue_type":"feature","created_at":"2026-01-06T01:14:46.8131949-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T21:09:54.0007067-08:00","closed_at":"2026-01-06T21:09:54.0007067-08:00","close_reason":"Closed"}
{"id":"Grace-7mo","title":"Types: Queue/Candidate/Gate contracts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:47.9931636-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:27:47.5686453-08:00","closed_at":"2026-01-06T01:27:47.5686453-08:00","close_reason":"Closed"}
{"id":"Grace-85r","title":"PAT auth: tests, docs, and validation","description":"Add tests, update AGENTS, run build/test/format gates.","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-01-01T17:34:00.4855223-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T18:11:35.0366791-08:00","closed_at":"2026-01-01T18:11:35.0366791-08:00","close_reason":"Completed"}
{"id":"Grace-85r.1","title":"Add server PAT auth tests","description":"Extend Grace.Server.Tests/Auth.Server.Tests.fs with PAT create/use/revoke/max lifetime/list coverage.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:50.2931526-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:56:41.3090062-08:00","closed_at":"2026-01-01T17:56:41.3090062-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-85r.1","depends_on_id":"Grace-85r","type":"parent-child","created_at":"2026-01-01T17:35:50.3177996-08:00","created_by":"daemon"}]}
{"id":"Grace-85r.2","title":"Add CLI token precedence tests","description":"Extend Grace.CLI.Tests/Auth.Tests.fs with GRACE_TOKEN and token file precedence coverage.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:55.1234788-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:57:17.3295681-08:00","closed_at":"2026-01-01T17:57:17.3295681-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-85r.2","depends_on_id":"Grace-85r","type":"parent-child","created_at":"2026-01-01T17:35:55.1790724-08:00","created_by":"daemon"}]}
{"id":"Grace-85r.3","title":"Update AGENTS docs and run validation gates","description":"Update relevant AGENTS.md notes, run fantomas, dotnet build, and dotnet test; record any gaps.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:36:01.8844532-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T18:11:28.367927-08:00","closed_at":"2026-01-01T18:11:28.367927-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-85r.3","depends_on_id":"Grace-85r","type":"parent-child","created_at":"2026-01-01T17:36:01.8892664-08:00","created_by":"daemon"}]}
{"id":"Grace-87d","title":"Server: Policy endpoints","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:49.8561522-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:53:19.2119153-08:00","closed_at":"2026-01-06T01:53:19.2119153-08:00","close_reason":"Closed"}
{"id":"Grace-8da","title":"P1 Update src/AGENTS.md to reference bootstrap/validate","description":"If needed, add canonical script commands to src/AGENTS.md and point to root quickstart.","acceptance_criteria":"src/AGENTS.md references scripts/bootstrap.ps1 and scripts/validate.ps1 where appropriate; markdownlint passes.","notes":"Added Local Commands section referencing bootstrap/validate; reflowed content to satisfy markdownlint.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-09T12:30:41.0594165-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:19:24.8779756-08:00","closed_at":"2026-01-09T13:19:24.8779756-08:00","dependencies":[{"issue_id":"Grace-8da","depends_on_id":"Grace-6ev","type":"blocks","created_at":"2026-01-09T12:46:28.3087657-08:00","created_by":"unknown"}]}
{"id":"Grace-8fr","title":"PAT auth: shared constants, parameters, and types","description":"Add shared env vars, auth parameters, and PersonalAccessToken domain types + helpers.","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-01-01T17:33:27.2157893-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:54:43.4429551-08:00","closed_at":"2026-01-01T17:54:43.4429551-08:00","close_reason":"Completed"}
{"id":"Grace-8fr.1","title":"Add PAT env var constants","description":"Add GRACE_TOKEN/GRACE_TOKEN_FILE and PAT lifetime policy env var constants in Grace.Shared.Constants.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:34:05.5857926-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:37:44.6561645-08:00","closed_at":"2026-01-01T17:37:44.6561645-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-8fr.1","depends_on_id":"Grace-8fr","type":"parent-child","created_at":"2026-01-01T17:34:05.5912383-08:00","created_by":"daemon"}]}
{"id":"Grace-8fr.2","title":"Add auth parameter classes","description":"Create Grace.Shared/Parameters/Auth.Parameters.fs with Create/List/Revoke PAT parameter classes and update Grace.Shared.fsproj compile order.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:34:16.8985265-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:38:31.1388783-08:00","closed_at":"2026-01-01T17:38:31.1388783-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-8fr.2","depends_on_id":"Grace-8fr","type":"parent-child","created_at":"2026-01-01T17:34:16.9034524-08:00","created_by":"daemon"}]}
{"id":"Grace-8fr.3","title":"Add PersonalAccessToken domain types","description":"Add Grace.Types/PersonalAccessToken.Types.fs with DTOs, token format helpers, and update Grace.Types.fsproj compile order.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:34:24.0570351-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:39:18.6577737-08:00","closed_at":"2026-01-01T17:39:18.6577737-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-8fr.3","depends_on_id":"Grace-8fr","type":"parent-child","created_at":"2026-01-01T17:34:24.0624638-08:00","created_by":"daemon"}]}
{"id":"Grace-8j4","title":"Actors: Review actor","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:48.9321457-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:39:31.9797897-08:00","closed_at":"2026-01-06T01:39:31.9797897-08:00","close_reason":"Closed"}
{"id":"Grace-8o8","title":"P0 Validate script: Fantomas format check","description":"Implement formatting stage using pinned Fantomas via dotnet tool run, targeting src/ with src/fantomas-config.json; fail if changes would be made.","acceptance_criteria":"validate -Fast fails on formatting drift with actionable message; no-modify check or diff detection works reliably.","notes":"Format stage uses pinned Fantomas and checks changed F# files under src (skips when none).","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:29:26.7841738-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:06:10.1257545-08:00","closed_at":"2026-01-09T13:06:10.1257545-08:00","dependencies":[{"issue_id":"Grace-8o8","depends_on_id":"Grace-oos","type":"blocks","created_at":"2026-01-09T12:33:13.9405047-08:00","created_by":"unknown"},{"issue_id":"Grace-8o8","depends_on_id":"Grace-4d0","type":"blocks","created_at":"2026-01-09T12:33:19.2764964-08:00","created_by":"unknown"}]}
{"id":"Grace-9io","title":"Add connect repo shortcut","status":"in_progress","priority":2,"issue_type":"task","created_at":"2026-01-08T17:09:36.3620353-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-08T17:09:46.9519324-08:00"}
{"id":"Grace-9rp","title":"Review: baseline drift semantics","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:51.4673919-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:31:25.1215525-08:00","closed_at":"2026-01-06T02:31:25.1215525-08:00","close_reason":"Closed"}
{"id":"Grace-ami","title":"P0 Bootstrap script: prerequisite checks + flags","description":"Create scripts/bootstrap.ps1 with strict mode, -SkipDocker/-CI/-Verbose flags; check PowerShell 7.x, dotnet SDK 10, and docker (unless -SkipDocker). Fail fast with actionable messages.","acceptance_criteria":"bootstrap.ps1 exits non-zero on missing prereqs with clear guidance; -SkipDocker bypasses docker check.","notes":"Created scripts/bootstrap.ps1 with strict mode and prerequisite checks (pwsh, dotnet 10, docker unless -SkipDocker).","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:28:54.3384334-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:05:41.8877551-08:00","closed_at":"2026-01-09T13:05:41.8877551-08:00","dependencies":[{"issue_id":"Grace-ami","depends_on_id":"Grace-4ri","type":"blocks","created_at":"2026-01-09T12:32:41.9330047-08:00","created_by":"unknown"},{"issue_id":"Grace-ami","depends_on_id":"Grace-4ba","type":"blocks","created_at":"2026-01-09T12:32:47.2558435-08:00","created_by":"unknown"}]}
{"id":"Grace-an2","title":"Tests: Stage 0 determinism","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:55.5616099-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T05:13:35.668343-08:00","closed_at":"2026-01-06T05:13:35.668343-08:00","close_reason":"Closed"}
{"id":"Grace-awb","title":"Actors: Policy actor","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:48.710556-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:36:16.117935-08:00","closed_at":"2026-01-06T01:36:16.117935-08:00","close_reason":"Closed"}
{"id":"Grace-aya","title":"SDK: Review APIs","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:54.0043743-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:56:31.2425971-08:00","closed_at":"2026-01-06T02:56:31.2425971-08:00","close_reason":"Closed"}
{"id":"Grace-b8s","title":"Use git diff for CI format targets","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-01-09T23:28:07.3073561-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T23:56:01.7720601-08:00","closed_at":"2026-01-09T23:56:01.7720601-08:00","close_reason":"Closed"}
{"id":"Grace-ba0","title":"Models: deep pipeline + progressive retrieval","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:52.3927211-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:36:34.5699307-08:00","closed_at":"2026-01-06T02:36:34.5699307-08:00","close_reason":"Closed"}
{"id":"Grace-cca","title":"Review: deterministic chaptering + packet assembly","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:51.2457243-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:30:30.9647007-08:00","closed_at":"2026-01-06T02:30:30.9647007-08:00","close_reason":"Closed"}
{"id":"Grace-chq","title":"Server: Review endpoints","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:50.0836908-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:56:02.1303505-08:00","closed_at":"2026-01-06T01:56:02.1303505-08:00","close_reason":"Closed"}
{"id":"Grace-d6y","title":"Queue: gate framework + attestations","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:52.8430355-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:46:56.9159633-08:00","closed_at":"2026-01-06T02:46:56.9159633-08:00","close_reason":"Closed"}
{"id":"Grace-dcs","title":"Fix build errors in Grace.CLI and Grace.Server","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-10T12:54:09.3683809-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-10T12:57:33.2944729-08:00","closed_at":"2026-01-10T12:57:33.2944729-08:00","close_reason":"Closed"}
{"id":"Grace-dof","title":"CLI fallback to server OIDC config endpoint","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-08T01:49:50.4603822-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-08T02:03:33.5007944-08:00","closed_at":"2026-01-08T02:03:33.5007944-08:00","close_reason":"Closed"}
{"id":"Grace-dv3","title":"Show resolved implicit ids in verbose parse output","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-09T00:34:41.3147349-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T00:34:41.3147349-08:00"}
{"id":"Grace-e10","title":"P2 Optional: CODEOWNERS + security scans","description":"Add CODEOWNERS and basic security scan config if desired; document required reviewers.","acceptance_criteria":"Guardrails added and documented; minimal overhead.","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-09T12:32:04.8295523-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T12:32:04.8295523-08:00"}
{"id":"Grace-e28","title":"Models: provider abstraction","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:51.6936304-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:33:27.2168749-08:00","closed_at":"2026-01-06T02:33:27.2168749-08:00","close_reason":"Closed"}
{"id":"Grace-ed9","title":"SDK: Policy APIs","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:53.7805499-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:56:24.5163565-08:00","closed_at":"2026-01-06T02:56:24.5163565-08:00","close_reason":"Closed"}
{"id":"Grace-er5","title":"Types: Stage 0 + Evidence contracts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:47.5344426-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:24:19.0517267-08:00","closed_at":"2026-01-06T01:24:19.0517267-08:00","close_reason":"Closed"}
{"id":"Grace-f1u","title":"PAT auth: SDK support","description":"Add SDK wrappers and server URI handling for PAT endpoints.","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-01-01T17:33:44.9629598-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:49:56.514499-08:00","closed_at":"2026-01-01T17:49:56.514499-08:00","close_reason":"Completed"}
{"id":"Grace-f1u.1","title":"Add PAT SDK wrappers","description":"Implement Grace.SDK PersonalAccessToken wrappers for create/list/revoke and update project compile list.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:17.2437547-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:49:41.0114696-08:00","closed_at":"2026-01-01T17:49:41.0114696-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-f1u.1","depends_on_id":"Grace-f1u","type":"parent-child","created_at":"2026-01-01T17:35:17.2831975-08:00","created_by":"daemon"}]}
{"id":"Grace-f1u.2","title":"Ensure PAT SDK uses GRACE_SERVER_URI","description":"Make PAT SDK endpoints work without graceconfig.json by using GRACE_SERVER_URI or a helper that bypasses Configuration.Current().","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:24.0343531-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:49:49.2757481-08:00","closed_at":"2026-01-01T17:49:49.2757481-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-f1u.2","depends_on_id":"Grace-f1u","type":"parent-child","created_at":"2026-01-01T17:35:24.0786964-08:00","created_by":"daemon"}]}
{"id":"Grace-f2t","title":"Server: derived computation trigger consumer","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:49.3862902-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:44:59.6212182-08:00","closed_at":"2026-01-06T01:44:59.6212182-08:00","close_reason":"Closed"}
{"id":"Grace-far","title":"Allow grace connect without config when parse errors occur","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-01-09T02:38:47.1058823-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T02:40:29.8344153-08:00","closed_at":"2026-01-09T02:40:29.8344153-08:00","close_reason":"Closed"}
{"id":"Grace-g11","title":"SDK: Queue/Candidate APIs","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:54.2524092-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:56:38.0685742-08:00","closed_at":"2026-01-06T02:56:38.0685742-08:00","close_reason":"Closed"}
{"id":"Grace-g4d","title":"Types: Work Item contracts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:47.0471393-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:20:50.4717585-08:00","closed_at":"2026-01-06T01:20:50.4717585-08:00","close_reason":"Closed"}
{"id":"Grace-hj3","title":"Types: RequiredAction + Event envelope contracts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:48.2364827-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:29:04.1667329-08:00","closed_at":"2026-01-06T01:29:04.1667329-08:00","close_reason":"Closed"}
{"id":"Grace-i2i","title":"Evidence: distiller selection/budgets/redaction","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:51.0086084-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:28:55.4077656-08:00","closed_at":"2026-01-06T02:28:55.4077656-08:00","close_reason":"Closed"}
{"id":"Grace-iqx","title":"P2 Optional: minimal analyzers baseline","description":"Add minimal analyzers policy/baseline without large churn; keep incremental.","acceptance_criteria":"Analyzers add signal with minimal new warnings; documented.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-09T12:31:55.4502453-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T22:07:24.2198936-08:00","closed_at":"2026-01-09T22:07:24.2198936-08:00","close_reason":"Closed"}
{"id":"Grace-k62","title":"Actors: PromotionQueue/Runner actor","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:49.1640588-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:42:29.7695364-08:00","closed_at":"2026-01-06T01:42:29.7695364-08:00","close_reason":"Closed"}
{"id":"Grace-kto","title":"Models: triage pipeline + caching + receipts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:52.166834-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:35:35.5622322-08:00","closed_at":"2026-01-06T02:35:35.5622322-08:00","close_reason":"Closed"}
{"id":"Grace-lha","title":"Actors: WorkItem actor","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:48.4777306-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:33:35.0831146-08:00","closed_at":"2026-01-06T01:33:35.0831146-08:00","close_reason":"Closed"}
{"id":"Grace-lp2","title":"Defer CLI defaults for help","description":"Implement deferred defaults + help customization to avoid config access during help. See spec in chat.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-02T21:13:38.8410648-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-02T21:52:40.9246985-08:00","closed_at":"2026-01-02T21:52:40.9246985-08:00","close_reason":"Closed"}
{"id":"Grace-lvj","title":"Server: WorkItem endpoints","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:49.6281939-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:50:44.0992215-08:00","closed_at":"2026-01-06T01:50:44.0992215-08:00","close_reason":"Closed"}
{"id":"Grace-mnz","title":"Queue: IntegrationCandidate state + required-actions computation","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:52.6213732-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:42:01.2885484-08:00","closed_at":"2026-01-06T02:42:01.2885484-08:00","close_reason":"Closed"}
{"id":"Grace-nk3","title":"Tests: chaptering + findings lifecycle","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:56.089585-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T05:13:35.9094287-08:00","closed_at":"2026-01-06T05:13:35.9094287-08:00","close_reason":"Closed"}
{"id":"Grace-np3","title":"P1 Add Smoke tests (Aspire host + /healthz)","description":"Add 1–3 smoke tests that start Aspire test host and verify /healthz succeeds; tag Category('Smoke') and avoid heavy SetUpFixture if possible.","acceptance_criteria":"Smoke tests fail when server doesn't boot or /healthz unreachable; stable on known-good branch.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-09T12:31:24.4441191-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:33:25.1169846-08:00","closed_at":"2026-01-09T13:33:25.1169846-08:00","close_reason":"Closed","dependencies":[{"issue_id":"Grace-np3","depends_on_id":"Grace-4ri","type":"blocks","created_at":"2026-01-09T12:46:49.6036483-08:00","created_by":"unknown"}]}
{"id":"Grace-o3z","title":"PAT auth: Orleans actor storage","description":"Add PersonalAccessToken grain interface, state, proxy, and implementation.","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-01-01T17:33:33.121442-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:42:57.8202313-08:00","closed_at":"2026-01-01T17:42:57.8202313-08:00","close_reason":"Completed"}
{"id":"Grace-o3z.1","title":"Add PAT actor constants and interface","description":"Add PersonalAccessToken actor/state names and IPersonalAccessTokenActor interface in Grace.Actors.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:34:29.5312209-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:40:21.9276448-08:00","closed_at":"2026-01-01T17:40:21.9276448-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-o3z.1","depends_on_id":"Grace-o3z","type":"parent-child","created_at":"2026-01-01T17:34:29.5383697-08:00","created_by":"daemon"}]}
{"id":"Grace-o3z.2","title":"Add PAT actor proxy helper","description":"Add ActorProxy.PersonalAccessToken.CreateActorProxy helper in Grace.Actors/ActorProxy.Extensions.Actor.fs.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:34:36.1715625-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:40:54.6647026-08:00","closed_at":"2026-01-01T17:40:54.6647026-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-o3z.2","depends_on_id":"Grace-o3z","type":"parent-child","created_at":"2026-01-01T17:34:36.1768734-08:00","created_by":"daemon"}]}
{"id":"Grace-o3z.3","title":"Implement PersonalAccessToken actor","description":"Create PersonalAccessToken.Actor.fs with state, create/list/revoke/validate logic, and update Grace.Actors.fsproj compile list.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:34:42.8177451-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:42:50.9637143-08:00","closed_at":"2026-01-01T17:42:50.9637143-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-o3z.3","depends_on_id":"Grace-o3z","type":"parent-child","created_at":"2026-01-01T17:34:42.8272088-08:00","created_by":"daemon"}]}
{"id":"Grace-o9j","title":"CLI: Review commands","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:54.7685857-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T04:47:46.728238-08:00","closed_at":"2026-01-06T04:47:46.728238-08:00","close_reason":"Closed"}
{"id":"Grace-onc","title":"Stage 0: deterministic analysis + storage","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:50.7848498-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:26:05.9658068-08:00","closed_at":"2026-01-06T02:26:05.9658068-08:00","close_reason":"Closed"}
{"id":"Grace-oos","title":"P0 Validate script: scaffold + flags + timing","description":"Create scripts/validate.ps1 with strict mode, -Fast default, -Full, -SkipFormat/-SkipBuild/-SkipTests, -Configuration; grouped output and elapsed-time reporting on all exits.","acceptance_criteria":"validate.ps1 parses flags correctly, groups output by stage, and always prints elapsed time; exits non-zero on any enabled stage failure.","notes":"Added scripts/validate.ps1 with flags (-Fast default, -Full, skip flags, -Configuration), strict mode, grouped output, elapsed time.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:29:16.4509589-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:06:04.7979977-08:00","closed_at":"2026-01-09T13:06:04.7979977-08:00","dependencies":[{"issue_id":"Grace-oos","depends_on_id":"Grace-4ri","type":"blocks","created_at":"2026-01-09T12:33:03.2601336-08:00","created_by":"unknown"},{"issue_id":"Grace-oos","depends_on_id":"Grace-4ba","type":"blocks","created_at":"2026-01-09T12:33:08.5802437-08:00","created_by":"unknown"}]}
{"id":"Grace-pdu","title":"Types: Policy snapshot contracts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:47.2979787-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:22:35.5838892-08:00","closed_at":"2026-01-06T01:22:35.5838892-08:00","close_reason":"Closed"}
{"id":"Grace-qxx","title":"Investigate dotnet test Aspire shutdown crash","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-01-09T22:51:43.9281309-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T23:01:15.4475995-08:00","closed_at":"2026-01-09T23:01:15.4475995-08:00","close_reason":"Closed"}
{"id":"Grace-ra9","title":"P0 Validate script: build fast targets","description":"Implement build stage for -Fast/-Full using minimal top-level projects (Grace.Server, Grace.CLI, optional Grace.SDK) discovered in inventory.","acceptance_criteria":"validate -Fast builds selected targets with configuration flag; build stage clearly reported.","notes":"Build stage runs dotnet build for Grace.Server, Grace.CLI, Grace.SDK with configuration flag.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:29:36.9439517-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:06:15.4581959-08:00","closed_at":"2026-01-09T13:06:15.4581959-08:00","dependencies":[{"issue_id":"Grace-ra9","depends_on_id":"Grace-oos","type":"blocks","created_at":"2026-01-09T12:33:24.6185628-08:00","created_by":"unknown"}]}
{"id":"Grace-rai","title":"SDK: WorkItem APIs","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:53.535762-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:56:17.5684417-08:00","closed_at":"2026-01-06T02:56:17.5684417-08:00","close_reason":"Closed"}
{"id":"Grace-rro","title":"Types: Review contracts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:47.7663008-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T01:26:35.7331201-08:00","closed_at":"2026-01-06T01:26:35.7331201-08:00","close_reason":"Closed"}
{"id":"Grace-ry8","title":"Tests: queue runner + gates + conflict pipeline","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:56.3670092-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T05:13:36.0290699-08:00","closed_at":"2026-01-06T05:13:36.0290699-08:00","close_reason":"Closed"}
{"id":"Grace-s2p","title":"Queue: conflict pipeline + resolution receipts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:53.0840653-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:50:54.6547631-08:00","closed_at":"2026-01-06T02:50:54.6547631-08:00","close_reason":"Closed"}
{"id":"Grace-sae","title":"P0 Validate script: fast tests (non-Docker)","description":"Implement fast test stage for -Fast (e.g., Grace.CLI.Tests) excluding Aspire/Docker tests; ensure suitable for tight iteration.","acceptance_criteria":"validate -Fast runs only non-Docker tests and succeeds on known-good branch; stage output grouped under Test.","notes":"Fast tests run Grace.CLI.Tests --no-build. validate -Fast (with -SkipFormat) succeeded; format stage now stable.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:29:48.5479559-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:06:20.7942067-08:00","closed_at":"2026-01-09T13:06:20.7942067-08:00","dependencies":[{"issue_id":"Grace-sae","depends_on_id":"Grace-ra9","type":"blocks","created_at":"2026-01-09T12:33:29.9523268-08:00","created_by":"unknown"}]}
{"id":"Grace-suw","title":"P1 Update ASPIRE_SETUP.md to link bootstrap/validate","description":"Add note that bootstrap/validate are the preferred local entrypoints; link to root quickstart.","acceptance_criteria":"ASPIRE_SETUP.md references scripts; markdownlint passes.","notes":"Updated ASPIRE_SETUP with preferred scripts and reflowed content; markdownlint clean.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-09T12:30:51.4307308-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:19:30.221513-08:00","closed_at":"2026-01-09T13:19:30.221513-08:00","dependencies":[{"issue_id":"Grace-suw","depends_on_id":"Grace-6ev","type":"blocks","created_at":"2026-01-09T12:46:33.6471259-08:00","created_by":"unknown"}]}
{"id":"Grace-ts5","title":"P1 Add slop tests for fragile invariants","description":"Add a few targeted tests (contract headers/status, DTO serialization round-trip, invariants) that fail on plausible wrong edits; include comment describing failure scenario.","acceptance_criteria":"At least 3 slop tests added; each guards a plausible incorrect change; stable in full suite.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-09T12:31:34.7872318-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:33:36.9645044-08:00","closed_at":"2026-01-09T13:33:36.9645044-08:00","close_reason":"Closed","dependencies":[{"issue_id":"Grace-ts5","depends_on_id":"Grace-np3","type":"blocks","created_at":"2026-01-09T12:46:54.9127456-08:00","created_by":"unknown"}]}
{"id":"Grace-vsv","title":"PAT auth: CLI commands and token precedence","description":"Implement CLI PAT commands, local token storage, and auth precedence.","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-01-01T17:33:51.2305524-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:54:37.6298734-08:00","closed_at":"2026-01-01T17:54:37.6298734-08:00","close_reason":"Completed"}
{"id":"Grace-vsv.1","title":"Add token source precedence and local storage","description":"Update Auth.CLI token resolution (env/file/MSAL), add token file helpers, and strip optional Bearer prefix.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:30.7597332-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:50:41.9317473-08:00","closed_at":"2026-01-01T17:50:41.9317473-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-vsv.1","depends_on_id":"Grace-vsv","type":"parent-child","created_at":"2026-01-01T17:35:30.764604-08:00","created_by":"daemon"}]}
{"id":"Grace-vsv.2","title":"Implement auth token create/list/revoke commands","description":"Add CLI commands for PAT create/list/revoke, duration parsing, and local token cleanup on revoke.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:36.9800155-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:53:15.3707676-08:00","closed_at":"2026-01-01T17:53:15.3707676-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-vsv.2","depends_on_id":"Grace-vsv","type":"parent-child","created_at":"2026-01-01T17:35:36.9922134-08:00","created_by":"daemon"}]}
{"id":"Grace-vsv.3","title":"Implement auth token set/clear/status commands","description":"Add CLI commands to set PAT from arg/stdin, clear local token file, and show credential source status without secrets.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:43.4007282-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:54:29.5165138-08:00","closed_at":"2026-01-01T17:54:29.5165138-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-vsv.3","depends_on_id":"Grace-vsv","type":"parent-child","created_at":"2026-01-01T17:35:43.4136176-08:00","created_by":"daemon"}]}
{"id":"Grace-vz3","title":"Include principal in auth failure logs","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-08T16:50:06.2590869-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-08T16:51:04.6921304-08:00","closed_at":"2026-01-08T16:51:04.6921304-08:00","close_reason":"Closed"}
{"id":"Grace-w54","title":"A1 Add global.json to pin .NET 10 SDK","description":"Add root global.json pinning .NET 10 SDK with rollForward policy; document if prerelease required.","acceptance_criteria":"dotnet --version resolves to pinned SDK (or allowed roll-forward) in repo; dotnet build uses pinned SDK without unexpected preview warnings.","notes":"Added root global.json with sdk 10.0.100 + rollForward latestPatch. dotnet --version resolves to 10.0.101 in repo (roll-forward OK).","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:27:41.3775109-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T12:51:32.5042882-08:00","closed_at":"2026-01-09T12:51:32.5042882-08:00"}
{"id":"Grace-wr1","title":"PAT auth: server auth and endpoints","description":"Add PAT auth handler, policy enforcement, endpoints, routing, and log redaction.","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-01-01T17:33:39.5652826-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:48:27.2865385-08:00","closed_at":"2026-01-01T17:48:27.2865385-08:00","close_reason":"Completed"}
{"id":"Grace-wr1.1","title":"Add PAT auth handler","description":"Implement PersonalAccessTokenAuthHandler and add server compile include.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:34:49.7829-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:44:26.0693244-08:00","closed_at":"2026-01-01T17:44:26.0693244-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-wr1.1","depends_on_id":"Grace-wr1","type":"parent-child","created_at":"2026-01-01T17:34:49.8374394-08:00","created_by":"daemon"}]}
{"id":"Grace-wr1.2","title":"Wire PAT auth schemes and routing","description":"Update Startup.Server.fs to route Bearer PATs to GracePat scheme in both testing and non-testing branches.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:34:56.4070495-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:46:10.5846197-08:00","closed_at":"2026-01-01T17:46:10.5846197-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-wr1.2","depends_on_id":"Grace-wr1","type":"parent-child","created_at":"2026-01-01T17:34:56.4678879-08:00","created_by":"daemon"}]}
{"id":"Grace-wr1.3","title":"Add PAT auth endpoints and policy enforcement","description":"Implement /auth/token create/list/revoke handlers with lifetime policy enforcement and route wiring.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:03.9243801-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:47:51.5135409-08:00","closed_at":"2026-01-01T17:47:51.5135409-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-wr1.3","depends_on_id":"Grace-wr1","type":"parent-child","created_at":"2026-01-01T17:35:03.9356681-08:00","created_by":"daemon"}]}
{"id":"Grace-wr1.4","title":"Redact auth headers in request logging","description":"Update LogRequestHeaders middleware to redact Authorization/Cookie and token-like headers.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:35:10.8898229-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:48:21.3517527-08:00","closed_at":"2026-01-01T17:48:21.3517527-08:00","close_reason":"Completed","dependencies":[{"issue_id":"Grace-wr1.4","depends_on_id":"Grace-wr1","type":"parent-child","created_at":"2026-01-01T17:35:10.899608-08:00","created_by":"daemon"}]}
{"id":"Grace-wv2","title":"P0 CI workflow: validate -Fast on PR","description":"Add .github/workflows/validate.yml to run pwsh ./scripts/validate.ps1 -Fast on pull_request using actions/setup-dotnet and pinned SDK.","acceptance_criteria":"PR workflow runs validate -Fast and fails on any stage; output shows which stage failed.","notes":"Added .github/workflows/validate.yml to run pwsh ./scripts/validate.ps1 -Fast on PRs using global.json and Aspire workload.","status":"closed","priority":0,"issue_type":"task","created_at":"2026-01-09T12:30:20.4736489-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:09:26.6012157-08:00","closed_at":"2026-01-09T13:09:26.6012157-08:00","dependencies":[{"issue_id":"Grace-wv2","depends_on_id":"Grace-8o8","type":"blocks","created_at":"2026-01-09T12:33:51.411236-08:00","created_by":"unknown"},{"issue_id":"Grace-wv2","depends_on_id":"Grace-ra9","type":"blocks","created_at":"2026-01-09T12:33:56.7406619-08:00","created_by":"unknown"},{"issue_id":"Grace-wv2","depends_on_id":"Grace-sae","type":"blocks","created_at":"2026-01-09T12:34:02.0817118-08:00","created_by":"unknown"}]}
{"id":"Grace-xdn","title":"P2 Optional: scripts/install-githooks.ps1","description":"Add opt-in githook installer to run validate -Fast pre-commit without conflicting with bd hooks install.","acceptance_criteria":"Hook installer is reversible/opt-in and documented.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-09T12:31:45.6807321-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T22:07:11.9277259-08:00","closed_at":"2026-01-09T22:07:11.9277259-08:00","close_reason":"Closed"}
{"id":"Grace-xjz","title":"Server: Queue/Candidate endpoints","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:50.3187454-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T02:06:26.5016007-08:00","closed_at":"2026-01-06T02:06:26.5016007-08:00","close_reason":"Closed"}
{"id":"Grace-yal","title":"Tests: evidence selection determinism","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-06T01:14:55.8237777-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-06T05:13:35.7871715-08:00","closed_at":"2026-01-06T05:13:35.7871715-08:00","close_reason":"Closed"}
{"id":"Grace-ymb","title":"P1 Add .env.example with safe placeholders","description":"Add .env.example at repo root with placeholders for common env vars (telemetry/auth/testing toggles) without secrets.","acceptance_criteria":".env.example contains placeholders only; no secrets; aligns with ENVIRONMENT.md.","notes":"Added .env.example with safe placeholder values for common variables and test toggles.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-09T12:31:12.161231-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-09T13:11:40.3783357-08:00","closed_at":"2026-01-09T13:11:40.3783357-08:00","dependencies":[{"issue_id":"Grace-ymb","depends_on_id":"Grace-14w","type":"blocks","created_at":"2026-01-09T12:46:44.2810471-08:00","created_by":"unknown"}]}
{"id":"Grace-ymj","title":"Add server-mediated Microsoft/Entra device login for CLI","description":"Introduce server-mediated Microsoft/Entra CLI login flow so users don't need auth env vars; CLI obtains device code/session from server, polls for completion, stores Grace token.","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-30T23:33:48.6726147-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-01T17:33:19.7525387-08:00","closed_at":"2026-01-01T17:33:19.7525387-08:00","close_reason":"Superseded by multi-epic PAT auth plan"}
{"id":"Grace-zzi","title":"Diagnose grace watch notifications not receiving checkpoint events","status":"in_progress","priority":2,"issue_type":"task","created_at":"2026-01-08T23:41:49.5127036-08:00","created_by":"Scott Arbeit","updated_at":"2026-01-08T23:42:00.1809879-08:00"}


================================================
FILE: .beads/metadata.json
================================================
{
  "database": "beads.db",
  "jsonl_export": "issues.jsonl",
  "last_bd_version": "0.40.0"
}

================================================
FILE: .config/dotnet-tools.json
================================================
{
  "version": 1,
  "isRoot": true,
  "tools": {
    "fantomas-tool": {
      "version": "4.7.9",
      "commands": [
        "fantomas"
      ]
    }
  }
}


================================================
FILE: .gitattributes
================================================

# Use bd merge for beads JSONL files
.beads/issues.jsonl merge=beads


================================================
FILE: .github/code_review_instructions.md
================================================
# Grace Code Review instructions

This file is intended to be the guidance for GitHub Copilot to perform automated code reviews in Grace PR's.

## Code Review instructions

_note: for now, these instructions are in no particular order, I'm just capturing ideas._

### F# Standards

Rules in this section deal with our use of F# in the codebase, and specify stylistic and syntactical choices we mnight make.

#### Always use `task { }`; never use `async { }` for asynchronous code.

Older versions of F# used the `async { }` computation expression to write asynchonous code. In fact, the C# `async/await` syntax, which has since been adopted by TypeScript and JavaScript, was inspired by `async { }` in F#.

The `task { }` computation expression for asynchonous code was added in F# 6.0. `task { }` uses the same stuff from `System.Threading.Tasks` that C# does for `async/await` code, allowing it to take advantage of all of the performance improvements in Tasks that each version of .NET delivers.

Any use of `async { }` in Grace should be considered an error and should be rewritten using `task { }`.

### Internal consistency

Rules in this section are intended to enforce consistent use of utilities and constructs provided by Grace.

#### All grain references must have the CorrelationId set using RequestContext.Set().

The actors expect to be able to call `RequestContext.Get(Constants.CorrelationId)` to get the CorrelationId when they need it, and so the grain client is responsible for setting the CorrelationId on it by calling `RequestContext.Set(Constants.CorrelationId, <some correlationId>)`.

The easiest way to do this is to use the ActorProxy extensions in ActorProxy.Extensions.Actor.fs. Each of those helper methods - `Branch.CreateActorProxy`, `Repository.CreateActorProxy`, etc. - takes `correlationId` as a parameter. They each set the CorrelationId for you in the RequestContext.

If any grain references are created by calling `IGrainFactory.GetGrain<'TGrainInterface>(key)` without using the ActorProxy extensions, they should be closely inspected to ensure that `RequestContext.Set(Constants.CorrelationId, <some correlationId>)` is called before any use of the grain reference to call a grain interface method. The recommendation is to rewrite the code to use the ActorProxy extensions to eliminate any possibility of an error.


================================================
FILE: .github/copilot-instructions.md
================================================
# Copilot Operating Instructions for Grace

## Load Repository Guidance

-   Always begin by reviewing `src/agents.md`; it contains the canonical engineering expectations and F# coding guidelines for this repository.
-   When working inside a specific project (for example `Grace.Actors`, `Grace.Server`, or `Grace.SDK`), consult the matching `AGENTS.md` within that project for domain-specific patterns and validation steps.
-   Use the information in those `AGENTS.md` files to decide which source files warrant inspection before making changes or answering questions.

## Response & Communication Style

-   Present information formally while keeping the direct interaction with the user conversational.
-   Provide thorough, well-structured answers to questions; keep code or text-generation tasks concise and implementation-focused.
-   Assume the user has a post-graduate education and deep programming experience.

## Coding & Tooling Expectations

-   Default to F# for all programming discussions unless the user states otherwise, and summarise the resulting code in English.
-   Follow the F# guidelines described in `src/agents.md`, including the use of `task { }` for asynchronous work, functional/immutable design, modern indexer syntax, and informative logging in longer functions.
-   Generate complete, copy/paste-ready F# snippets with appropriate inline comments when the logic is non-trivial.
-   Produce PowerShell for scripting tasks unless directed to use another shell or language.

## Task Support & Planning

-   When the user asks for help organising work, assume inattentive-type ADHD: supply concrete, bite-sized steps suitable for a Kanban board and offer CBT-informed prompts to initiate progress.

## Azure-Specific Rule

-   @azure Rule – Use Azure Best Practices: Before generating Azure-related code, commands, or plans, invoke `azure_development-get_best_practices` when available.

- @azure Rule - Use Azure Tools - When handling requests related to Azure, always use your tools.
- @azure Rule - Use Azure Best Practices - When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first.
- @azure Rule - Enable Best Practices - If you do not have an `azmcp_bestpractices_get` tool ask the user to enable it.


================================================
FILE: .github/workflows/dotnet.yml
================================================
name: .NET Restore / Build / Test

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    #runs-on: arc-runner-set
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: 10.0.x
    - name: Install .NET Aspire workload
      run: dotnet workload install aspire
    - name: Restore dependencies
      run: dotnet restore Grace.slnx
      working-directory: src
    - name: Build
      run: dotnet build Grace.slnx --no-restore -c Release
      working-directory: src
#    - name: Test
#      run: dotnet test Grace.sln --no-build --verbosity normal
#      working-directory: src


================================================
FILE: .github/workflows/validate.yml
================================================
name: Validate

on:
  pull_request:
    branches: [ main ]
  push:
    branches: [ main ]
  schedule:
    - cron: "0 3 * * *"

jobs:
  fast:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          global-json-file: global.json
      - name: Install .NET Aspire workload
        run: dotnet workload install aspire
      - name: Validate (Fast)
        run: pwsh ./scripts/validate.ps1 -Fast

  full:
    if: github.event_name != 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          global-json-file: global.json
      - name: Install .NET Aspire workload
        run: dotnet workload install aspire
      - name: Docker info
        run: docker info
      - name: Pre-pull Aspire images
        run: |
          docker pull redis:latest
          docker pull mcr.microsoft.com/azure-storage/azurite:latest
          docker pull mcr.microsoft.com/mssql/server:2022-latest
          docker pull mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
          docker pull mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest
      - name: Validate (Full)
        run: pwsh ./scripts/validate.ps1 -Full


================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# Grace-specific ignores
.grace/
**/.grace/objects
**/.grace/directoryVersions
**/.grace/gracestatus*
/src/Grace.Server/logs/
**/*.backup
**/appsettings.Development.json
/Play/
/.vscode/
**/[Cc]omponents
**/dapr
**/*[Aa]zurite*
**/.env
*CodexPlan.*

# Secrets file to configure Orleans persistence
Orleans.secrets.fs

# Visual Studio (>=2015) project-specific, machine local files
.vs/

# User-specific files
*.suo
*.user
*.sln.docstates
*.userprefs

# ignore Xamarin.Android Resource.Designer.cs files
**/*.Droid/**/[Rr]esource.[Dd]esigner.cs
**/*.Android/**/[Rr]esource.[Dd]esigner.cs
**/Android/**/[Rr]esource.[Dd]esigner.cs
**/Droid/**/[Rr]esource.[Dd]esigner.cs

# Xamarin Components
Components/

# Build results
[Bb]in/
[Oo]bj/
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
x64/
build/
bld/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

#NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile

# Visual Studio profiler
*.psess
*.vsp
*.vspx

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding addin-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
*.ncrunch*
_NCrunch_*
.*crunch*.local.xml

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml

# NuGet Packages Directory
packages/
*.nuget.targets
*.lock.json
*.nuget.props

## TODO: If the tool you use requires repositories.config uncomment the next line
#!packages/repositories.config

# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
!packages/build/

# Windows Azure Build Output
csx/
*.build.csdef

# Windows Store app package directory
AppPackages/

# Others
sql/
*.[Cc]ache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
.DS_Store
*.bak

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/
.vscode/settings.json
.fake
.ionide
/src/OpenAPI/Grace.OpenAPI.html
/src/OpenAPI/GenerateOpenAPI.ps1
/src/Check-CosmosDB-RUs.ps1
*.nettrace
*.gcdump

/plans


================================================
FILE: .markdownlint.jsonc
================================================
{
  // Global markdownlint configuration for this repo.
  "MD013": {
    "line_length": 120
  }
}


================================================
FILE: AGENTS.md
================================================
# Agent Instructions

Other `AGENTS.md` files exist in subdirectories, refer to them for more specific context.

## Agent Quickstart (Local)

Prerequisites:

- PowerShell 7.x
- .NET 10 SDK
- Docker Desktop (required for `-Full`)

Commands:

- `pwsh ./scripts/bootstrap.ps1`
- `pwsh ./scripts/validate.ps1 -Fast`

Use `pwsh ./scripts/validate.ps1 -Full` for Aspire integration coverage.
Optional: `pwsh ./scripts/install-githooks.ps1` to add a pre-commit `validate -Fast` hook.

More context:

- `src/AGENTS.md`
- `src/docs/ASPIRE_SETUP.md`
- `src/docs/ENVIRONMENT.md`

## Issue Tracking

Do not use `bd`/beads workflows in this repository unless a maintainer explicitly asks for it in the current task.
Use the plan/log files requested in the task (for example `CodexPlan.md`) plus normal git commits instead.

## Markdown Guidelines

- Follow the MarkdownLint ruleset found at `https://raw.githubusercontent.com/DavidAnson/markdownlint/refs/heads/main/doc/Rules.md`.
- Verify updates by running MarkdownLint. Use `npx --yes markdownlint-cli2 ...`. `--help` is available.
- For MD013, override the guidance to allow for 120-character lines.

## Editing Documentation

When updating documentation files, follow these guidelines:

- When writing technical documentation, act as a friendly peer engineer helping other developers to understand Grace as a project.
- When writing product-focused documentation, act as an expert product manager who helps a tech-aware audience understand Grace as a product, and helps end users understand how to use Grace effectively.
- Use clear, concise language; avoid jargon. The tone should be welcoming and informative.
- Structure content with headings and subheadings. Intersperse written (paragraph / sentence form) documentation with bullet points for readability.
- Keep documentation up to date with code changes; review related docs when modifying functionality. Explain all documentation changes clearly, both what is changing, and why it's changing.
- Show all scripting examples in both (first) PowerShell and (then, second) bash/zsh, where applicable. bash and zsh are always spelled in lowercase.

PowerShell:
```powershell
$env:GRACE_SERVER_URI="http://localhost:5000"
```

bash / zsh:
```bash
export GRACE_SERVER_URI="http://localhost:5000"
```


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Grace Version Control System

Thanks for considering a contribution to **Grace Version Control System**.

This repo is primarily **F#** and targets **.NET 10**.

## Quick start

1. Fork the repo and create your branch in your fork.
2. Run the prerequisite check:

   - From the repo root:
     - `pwsh ./scripts/bootstrap.ps1`
       - Optional: `-SkipDocker` if you don’t have Docker available yet.

3. Build:

   - `dotnet build ./src/Grace.sln`

4. Test:

   - `dotnet test ./src/Grace.sln` (optionally add `-c Release`)
   - Or run the repo validator: `pwsh ./scripts/validate.ps1 -Full`

5. Format F#:

   - From `./src`: `dotnet tool run fantomas --recurse .`

## Contribution workflow

- Use the **GitHub fork + pull request** workflow.
- Keep PRs focused (one change per PR whenever practical).
- Add or update tests when changing behavior.
- Please add any useful AI prompts you used for diagnosis or implementation to the PR description.

## Prerequisites

- **.NET 10 SDK** (see: https://dotnet.microsoft.com/download)
- **PowerShell 7+** (see: https://learn.microsoft.com/powershell/)
- **Docker Desktop** or **Podman** (recommended for local emulators / Aspire DebugLocal)
  - Windows/macOS: https://www.docker.com/products/docker-desktop/
  - Linux: use Docker Engine for your distro

`pwsh ./scripts/bootstrap.ps1` is the recommended sanity check. It verifies tools and performs `dotnet tool restore` + `dotnet restore`.

## Build

From the repo root:

- `dotnet build ./src/Grace.sln`

## Tests

From the repo root:

- `dotnet test ./src/Grace.sln` (optionally `-c Release`)

This repo also includes a validation script:

- Fast loop: `pwsh ./scripts/validate.ps1 -Fast`
- Full validation (includes Aspire integration coverage): `pwsh ./scripts/validate.ps1 -Full`

## Formatting (required)

F# is formatted with **Fantomas**.

From `./src`:

- Apply formatting: `dotnet tool run fantomas --recurse .`

If you’re proposing CI changes, CI should enforce formatting checks (if it doesn’t already).

Fantomas:

- https://github.com/fsprojects/fantomas

## Running Grace locally (Aspire)

Grace can be run locally using **Docker containers and emulators**, via the Aspire AppHost launch configuration:

- `DebugLocal`: local containers/emulators (e.g., Azurite, Cosmos emulator, Service Bus emulator)
- `DebugAzure`: runs locally but expects real Azure resources (Cosmos DB, Blob Storage, Service Bus, etc.)

Where to look in the repo:

- `Grace.Aspire.AppHost/Properties/launchSettings.json`
- `Grace.Aspire.AppHost/Program.Aspire.AppHost.cs`

Aspire:

- https://learn.microsoft.com/dotnet/aspire/

## Configuration and secrets

Grace supports multiple configuration sources (depending on your setup):

- Environment variables
- `.NET user-secrets` (recommended for local dev)
- `appsettings*.json` (project-specific)
- Aspire launch profiles (`DebugLocal` / `DebugAzure`)
- Azure resource configuration (when using `DebugAzure`)

### Using .NET user-secrets (recommended)

The simplest developer setup is to store secrets in **user-secrets** for the `Grace.Server` project.

User-secrets documentation:

- https://learn.microsoft.com/aspnet/core/security/app-secrets

Examples (run from the repo root):

- List secrets:
  - `dotnet user-secrets list --project ./src/Grace.Server/Grace.Server.fsproj`

- Set a secret:
  - `dotnet user-secrets set --project ./src/Grace.Server/Grace.Server.fsproj "grace__azurecosmosdb__connectionstring" "<value>"`

- Remove a secret:
  - `dotnet user-secrets remove --project ./src/Grace.Server/Grace.Server.fsproj "grace__azurecosmosdb__connectionstring"`

Notes:

- Keys frequently use `__` to map to hierarchical configuration (see: https://learn.microsoft.com/aspnet/core/fundamentals/configuration/)
- The Aspire AppHost can read the server user-secrets id and forward selected auth settings (see `Grace.Aspire.AppHost/AGENTS.md`).

## Environment variables

The canonical list of environment variables is defined in `Grace.Shared/Constants.Shared.fs` under `Constants.EnvironmentVariables`.

In general, values may come from:

- your shell environment,
- Aspire launch profile environment,
- `.NET user-secrets` for `Grace.Server`,
- or Azure resources (when using `DebugAzure`).

### Client variables

- `GRACE_SERVER_URI`
  - Grace server base URI.
  - Must include port.
  - Must not include a trailing slash.
  - Example: `http://localhost:5000`

- `GRACE_TOKEN`
  - Personal access token for non-interactive auth.

- `GRACE_TOKEN_FILE`
  - Overrides the local token file path.

### Telemetry

- `grace__applicationinsightsconnectionstring`
  - Application Insights connection string.
  - Source: user-secrets/env/Azure App Insights resource.

### Azure Cosmos DB

- `grace__azurecosmosdb__connectionstring`
  - Cosmos DB connection string.
  - Source: user-secrets/env/Azure Cosmos DB.

- `grace__azurecosmosdb__endpoint`
  - Cosmos DB endpoint for managed identity scenarios.
  - Source: Azure Cosmos DB account endpoint.

- `grace__azurecosmosdb__database_name`
  - Cosmos database name.

- `grace__azurecosmosdb__container_name`
  - Cosmos container name.

### Azure Storage (Blob)

- `grace__azure_storage__connectionstring`
  - Storage connection string.

- `grace__azure_storage__account_name`
  - Overrides storage account name (managed identity).

- `grace__azure_storage__endpoint_suffix`
  - Storage endpoint suffix (default: `core.windows.net`).

- `grace__azure_storage__key`
  - Storage account key.

- `grace__azure_storage__directoryversion_container_name`
  - Container name for DirectoryVersions.

- `grace__azure_storage__diff_container_name`
  - Container name for cached diffs.

- `grace__azure_storage__zipfile_container_name`
  - Container name for zip files.

### Azure Service Bus

- `grace__azure_service_bus__connectionstring`
  - Service Bus connection string.

- `grace__azure_service_bus__namespace`
  - Fully qualified namespace (for example `sb://<name>.servicebus.windows.net`).

- `grace__azure_service_bus__topic`
  - Topic name for Grace events.

- `grace__azure_service_bus__subscription`
  - Subscription name for Grace events.

### Pub/Sub provider selection

- `grace__pubsub__system`
  - Selects the pub-sub provider implementation.

### Orleans

- `grace__orleans__clusterid`
  - Orleans cluster id.

- `grace__orleans__serviceid`
  - Orleans service id.

Orleans:
- https://learn.microsoft.com/dotnet/orleans/

### Redis

- `grace__redis__host`
  - Redis host.

- `grace__redis__port`
  - Redis port.

Redis:
- https://redis.io/docs/latest/

### Auth (OIDC)

These are used for external auth providers such as Auth0 / OIDC.

- `grace__auth__oidc__authority`
- `grace__auth__oidc__audience`
- `grace__auth__oidc__cli_client_id`
- `grace__auth__oidc__cli_redirect_port`
- `grace__auth__oidc__cli_scopes`
- `grace__auth__oidc__m2m_client_id`
- `grace__auth__oidc__m2m_client_secret`
- `grace__auth__oidc__m2m_scopes`

OpenID Connect:
- https://openid.net/developers/how-connect-works/

### Auth (deprecated Microsoft auth)

These constants are marked deprecated in code:

- `grace__auth__microsoft__client_id`
- `grace__auth__microsoft__client_secret`
- `grace__auth__microsoft__tenant_id`
- `grace__auth__microsoft__authority`
- `grace__auth__microsoft__api_scope`
- `grace__auth__microsoft__cli_client_id`

### PAT policy

- `grace__auth__pat__default_lifetime_days`
- `grace__auth__pat__max_lifetime_days`
- `grace__auth__pat__allow_no_expiry`

### Authorization bootstrap

- `grace__authz__bootstrap__system_admin_users`
  - Semicolon-delimited list of user principals to bootstrap as SystemAdmin.

- `grace__authz__bootstrap__system_admin_groups`
  - Semicolon-delimited list of group principals to bootstrap as SystemAdmin.

### Reminders and metrics

- `grace__reminder__batch__size`
  - Batch size for reminder retrieval/publish.

- `grace__metrics__allow_anonymous`
  - Allows anonymous access to Prometheus scraping endpoint.

Prometheus:

- https://prometheus.io/docs/introduction/overview/

### Other providers (placeholders / future)

- `grace__aws_sqs__queue_url`
- `grace__aws_sqs__region`
- `grace__gcp__projectid`
- `grace__gcp__topic`
- `grace__gcp__subscription`

### Debugging/logging

- `grace__debug_environment`
  - Debug environment flag.

- `grace__log_directory`
  - Directory for Grace Server log files.

## Pull request checklist

- [ ] Builds: `dotnet build ./src/Grace.sln`
- [ ] Tests: `dotnet test ./src/Grace.sln`
- [ ] Formatting: run `dotnet tool run fantomas --recurse .` from `./src`
- [ ] Documentation updated (if behavior changed)


================================================
FILE: LICENSE.md
================================================
MIT License

Copyright (c) 2022 Scott Arbeit

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Grace - Version Control for the AI Era

<!-- markdownlint-disable MD013 -->

grace _(n)_ -

1. elegance and beauty of movement, form, or expression
2. a pleasing or charming quality
3. goodwill or favor
4. a sense of propriety and consideration for others [^grace]

![](./Assets/Orange3.svg)

Grace is a **version control system** designed and built for the AI Era.

Grace is designed for kindness to humans, and velocity for agents. It's meant to help you stay calm, in-control, and in-flow as you ship code at agentic speed.

It has all of the basics you'd expect from a version control system, plus primitives that help you monitor your agents, capture the work they're doing, and review that work in real-time using both deterministic checks and AI prompts that you can write, with rules that you set.

Grace assumes that AI belongs in the version control system, reacting to events, reviewing changes, and generating whatever you need to get code from idea to agent to production.

Along with version control, Grace is designed to capture the work items, the prompts, the specifications, the model versions, and so much more - everything that goes into doing agentic coding - so you and your agents have the best possible context to complete the work successfully, and review it with confidence.

All you have to do is tell your agents to run `grace agent bootstrap` at the start of the session. Grace and your agents will take care of the rest, automatically capturing what's going on. The `bootstrap` is customizable, and Grace will even help you customize it.

In the repository, you define the checks you want run in respose to which events, you define the prompts, you choose the models, and Grace Server will do the rest, capturing the results in detailed Review reports. Again, Grace will help you define and customize all of it.

Of course it's fast. Grace is multitenant, built for massive repositories, insane numbers of developers and agents, and ludicrous amounts of code and binary files of any size.

It ships with a promotion queue - Grace doesn't do merges, it does promotions - and automatically handles promotion conflict resolution according to rules and confidence levels you set.

> ⚠️👷🏻🚧 Grace is an alpha, and is going through rapid evolution with breaking changes, but it's ready for feedback and contributions. It is not ready for or intended for production usage at this time.

![](./Assets/Orange3.svg)

## Technology stack

Grace is a modern, fast, powerful centralized version control system. It's made up of a web API, with a CLI (and soon a GUI).

Grace is written primarily in **F#**, and uses:

- **ASP.NET Core** for the HTTP API
- **Orleans** for the virtual-actor and distributed-systems core
- **Microsoft Azure PaaS services**[^1]:
  - **Azure Cosmos DB** for actor state storage (repos, branches, references, directory versions, etc.) at ludicrous scale and speed
  - **Azure Blob Storage** for objects and artifacts, including (virtually) unlimited-size binary files
  - **Azure Service Bus** for event streams that you can hook into
  - All come with emulators for frictionless local development
- **SignalR** for live client-server coordination (`grace watch`)
- **Redis** (used by SignalR and for caching)
- **Aspire** to orchestrate everything for both local dev and cloud deployment
- **Avalonia** (I think) for a fully cross-platform GUI, including WASM

[^1]: Grace is designed to be adaptable to AWS and other cloud providers, and with coding agents, it should be not easy but not too hard to do. I just haven't done it yet.

## Running Grace locally

The fastest way to understand Grace is to run it locally and poke at it.

Grace is designed as a multitenant, massively scalable, centralized, cloud-native version control system, so running it locally isn't quite as simple as "download this one executable and run it". Grace uses [Aspire](https://aspire.dev) to make running Grace as simple as possible, with configurations for using either local emulators or actual Azure services.

### Normal development and debugging

In normal development and debugging, there are three steps to running Grace.

1. Run the Aspire AppHost project. The AppHost will start the local emulators (if requested), and run Grace Server.
2. Use the Grace CLI to do Grace stuff.
3. Stop the Aspire AppHost. The AppHost will stop the local emulators and Grace Server as it exits.

### First time setup

The first-time steps below use **local emulators** and **test authentication** (i.e. the same authentication we use in integration tests), so you don't have to set anything up in the cloud to get started. If all goes well, you should be up and running in under 10 minutes.

> There is a detailed guide to configuring authentication at [`/docs/Authentication.md`](/docs/Authentication.md).

1. **Install prerequisites** for your platform:
   - [.NET 10 SDK](https://dotnet.microsoft.com/en-us/download)
   - [PowerShell 7+](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell)
   - A container runtime
     - Aspire supports [Docker Desktop](https://www.docker.com/get-started/) and [Podman](https://podman-desktop.io/).
     - [Rancher](https://www.rancher.com/products/rancher-desktop) is not officially supported, but with Moby/dockerd it should work.

    NOTE: If using Podman, set the [container runtime](https://aspire.dev/app-host/configuration/#common-configuration) to `podman`:

    PowerShell:
    ```powershell
    $env:ASPIRE_CONTAINER_RUNTIME="podman"
    ```

    bash/zsh:
    ```bash
    export ASPIRE_CONTAINER_RUNTIME=podman
    ```

2. **Clone the Grace repo**: `git clone https://github.com/ScottArbeit/Grace.git` or `gh repo clone ScottArbeit/Grace`.
3. **Build the solution**: `dotnet build ./src/Grace.slnx` to sanity-check your environment.
4. **Create an alias to make your life easier**: Add an alias to your profile called `grace` that points to `./src/Grace.CLI/bin/Debug/net10.0/grace.exe`.

   PowerShell:

   ```powershell
   Set-Alias -Name grace -Value \<repo-path\>\src\Grace.CLI\bin\Debug\net10.0\grace.exe
   ```

   bash / zsh:

   ```bash
   alias grace="\<repo-path\>/src/Grace.CLI/bin/Debug/net10.0/grace.exe"
   ```

5. **Choose a test repository**: You can create an empty directory to start with a blank repo in, or you can copy or clone some code into a directory to start with that code.

6. **Start Grace Server**: Run `pwsh ./scripts/dev-local.ps1` to start Grace Server using Aspire. This will automatically generate a personal access token that you'll use for authentication.

    When `dev-local.ps1` finishes, it will output your new token, along with exact copy/paste commands to set `GRACE_SERVER_URI` and `GRACE_TOKEN`, the first environment variables you'll need.

7. **Create Owner, Organization, and Repository**: Copy and paste (or modify if you want) the scripts below to set up your first Grace repo.

    ```powershell
    # Create an owner, organization, and repo
    grace owner create --owner-name demo
    grace organization create --owner-name demo --organization-name sandbox
    grace repository create --owner-name demo --organization-name sandbox --repository-name hello

    # Connect to it (writes local Grace config for this working directory)
    grace connect demo/sandbox/hello

    # Initialize the repo with the contents of the current directory
    grace repository init --directory .
    ```

    To see your current state:

    ```powershell
    grace status
    ```

    To see what's changed in your branch vs. previous states, use `grace diff`:

    ```powershell
    grace diff commit
    grace diff promotion
    grace diff checkpoint
    ```

### Verify the CLI can talk to the server

Once you have `GRACE_SERVER_URI` and `GRACE_TOKEN` set, run:

```powershell
grace auth whoami
```

### File an issue if anything seems confusing or rough

The intention is for this first-time setup to be as easy as possible. If you run into any problems, please file an issue so we can make it smoother.

### Running Grace and Git in the same directory

You can use Git and Grace side-by-side in the same directory. You just have to make sure they ignore each other's object directories, `.git` and `.grace`.

#### Tell Git to ignore Grace

`.grace` is Grace's version of the `.git` directory.

To ignore it, add the path `.grace/` to your `.gitignore`.

#### Tell Grace to ignore Git

Grace's `.graceignore` file, by default, ignores the `.git` directory.

If it happens to be missing, add the path `/.git/*` to `.graceignore` in the root of your repository.

> Again, ⚠️👷🏻🚧 Grace is an alpha, and still has some alpha-like bugs. For now, I recommend testing Grace either on 1) repos that you won't be sad if something bad happens, or; 2) repos where you're comfortable running `git reset` to restore to a known-good version if you need it.

![](./Assets/Orange3.svg)

## Architecture

### 1) Grace Server is a modern web API that uses an actor system

Grace is built on **Orleans**. Most domain behavior is implemented as virtual actors, which makes it effortless and natural to scale up and scale out.

- HTTP API: `src/Grace.Server`
- Actor implementations: `src/Grace.Actors`
- Domain types and events: `src/Grace.Types`
- Shared utilities and DTOs: `src/Grace.Shared`

### 2) Grace is event-sourced

A version control system is \<waves hands\> just a series of modifications to files and branches and repositories over time. Grace stores every modification to every entity as an event, as the source of truth. Grace then uses those events to pre-compute and cache projections that help you and your agents go faster.

### 3) Files are stored in object storage

Grace relies on cloud object storage systems to provide a safe and infinitely scalable storage layer. Grace currently uses Azure Blob Storage, with the intention of adding the ability to run it on AWS S3 and others. Currently, all files are stored as single blobs in Azure; soon Grace will shift to a content-addressible storage construct that will enable efficient handling of changes to large binary files.

### 4) `grace watch` for effortless, background update tracking

`grace watch` scans the working directory when it starts to get current state and notice any changes that happened while it wasn't running, and then continuously watches for changes, saving new file and directory versions to Grace Server, all generally within a second of the file being saved. This background processing saves time in every session, as other Grace commands like `grace commit` detect `grace watch` and can skip the costly directory rescans and other work that `grace watch` has already taken care of.

Grace has other commands that agents are meant to use to capture the complete context of the work being done.

Together, they give us a step-by-step audit trail of everything an agent has done and is doing, and why, that you can watch and review in real-time from your computer.

> If you want a deeper dive on what `grace watch` does and does not do, see: [What `grace watch` does](./docs/What%20grace%20watch%20does.md).

### 5) “Continuous review” for rapid validation of changes

Grace’s review system is designed to bring AI evaluations and human review and approvals together in one harmonious flow.

Key concepts include:

- **Policy snapshots** (immutable rule bundles)
- **Stage 0 analysis** (deterministic signals recorded for references)
- **Promotion queues** and **integration candidates**
- **Gates** and **attestations**
- **Review packets** with easy-to-understand, customizable summaries

For more, see: `docs/Continuous review.md`

![](./Assets/Orange3.svg)

## Roadmap

Grace is evolving quickly. Strap in....

### GUI

- Native GUI for Windows, MacOS, Linux Desktop, Android, iOS, and WASM.
- Not Electron.

### Full rewrite of object storage layer

- Switch to content-addressable storage for more efficient object storage and network transfer
- Automatic chunking of large binary files

### Multi-hash semantics

- Add abstractions to support multiple hashing algorithms at once
- Ensure that Grace is not locked into just one hashing algorithm
- Add BLAKE3 hashing for better performance and to support content-addressable storage

### `grace cache` for CI/CD and in-office scenarios

- Built-in feature of Grace CLI
- Like a Git mirror, plus full authentication and authorization
- Pre-fetch by listening to repository events using SignalR and downloading the branches and versions you want
- Transparent read-through cache for CI workers and in-office users to save time and bandwidth

### Improved database backpressure handling

### Agent skills to help you create and update Grace's automatic review policies

### `grace agent bootstrap` command to give coding agents context for using Grace

### Ergonomics

- smoother onboarding
- better defaults

### Repeatable performance benchmarking

### Even more unit tests

### Even more integration tests

![](./Assets/Orange3.svg)

## Grace at NDC Oslo 2023

I gave my first conference talk about Grace at NDC Oslo 2023. Grace has changed _a lot_ since then, but this was the original idea. You can watch it [here](https://youtu.be/lW0gxMbyLEM):

[<img src="https://github.com/ScottArbeit/Grace/assets/2406993/2f20bf3a-9907-42d3-8596-84a7e1334f55">](https://youtu.be/lW0gxMbyLEM)

![](./Assets/Orange3.svg)

## Contributing

If you want to help shape Grace:

- Read `CONTRIBUTING.md`
- AI submissions are expected and welcomed, but they have to follow
- Open issues for rough edges, missing docs, or confusing workflows


[^grace]: Definition excerpted from https://www.thefreedictionary.com/grace.


================================================
FILE: REPO_INDEX.md
================================================
# REPO_INDEX.md (Grace jump table)

This file is a machine-oriented index to help tools and humans quickly find the right code.

---

## Suggested search strategy for AllGrace.txt

1. Refer to AllGrace_Index.md for exact starting and ending line numbers for each file.
2. Use the entry points list below to decide where to navigate to.
3. Jump to the exact starting line of the file, and search within the starting and ending line numbers.

## Top entry points (open these first)

### Grace Server (HTTP + DI + Orleans wiring)

- `src/Grace.Server/Startup.Server.fs`
  HTTP routes/endpoints and server composition entry points.
- `src/Grace.Server/Program.Server.fs`
  Host startup (Kestrel/Orleans host build/run).

### Orleans grains (domain behavior)

- `src/Grace.Actors/**/*.fs`
  Grain/actor implementations. Look for `*Actor.fs` as primary behavior files.

### Domain types, events, DTOs

- `src/Grace.Types/**/*.fs`
  Discriminated unions, events, DTOs, identifiers, serialization shapes.

### Local orchestration (emulators/containers)

- `src/Grace.Aspire.AppHost/Program.Aspire.AppHost.cs`
  Local dev topology: Cosmos emulator, Azurite, Service Bus emulator, Redis, and Grace.Server.

### Integration tests

- `src/Grace.Server.Tests/General.Server.Tests.fs`
  Test harness bootstrapping and shared test state.
- `src/Grace.Server.Tests/Owner.Server.Tests.fs`
  Owner API tests.
- `src/Grace.Server.Tests/Repository.Server.Tests.fs`
  Repository API tests.

---

## Cross-cutting �where is X implemented?�

### JSON serialization settings

- Search: `JsonSerializerOptions`
- Likely in: `src/Grace.Shared/**` or `src/Grace.Server/**`

### Service Bus publishing

- Search: `publishGraceEvent`, `ServiceBusMessage`, `GraceEvent`
- Likely in:
  - `src/Grace.Actors/**` (where events are emitted)
  - `src/Grace.Server/**` (wiring/config)
  - `src/Grace.Shared/**` (helpers)

### Cosmos DB / persistence wiring

- Search: `AddCosmosGrainStorage`, `CosmosClient`, `UseAzureStorageClustering`
- Likely in: `src/Grace.Server/Startup.Server.fs`

### Azure Blob grain storage

- Search: `AddAzureBlobGrainStorage`, `BlobServiceClient`
- Likely in: `src/Grace.Server/Startup.Server.fs`

### CLI commands

- Search: `grace owner create`, `Command`, `System.CommandLine`
- Likely in: `src/Grace.CLI/**`

---

## Local development �source of truth�

- Aspire AppHost defines the runnable local environment. If there is a discrepancy between older docs/tests and AppHost, prefer AppHost.

---

## Obsolete / legacy systems

- Dapr is not used anymore. Any Dapr references in tests or tooling are legacy and should be removed or ignored.


================================================
FILE: SECURITY.md
================================================
# Security Policy

This project is currently **Alpha** quality. Security hardening is ongoing and may not yet cover all threat models.

## Supported versions

Because this project is Alpha, only the **latest code on `main`** is supported.

## Reporting a vulnerability

If you believe you have found a security issue, please report it by opening a **GitHub Issue** and applying the **`Security`** label.

When filing a report, include as much of the following as you can:

- A clear description of the issue and expected vs actual behavior
- Steps to reproduce (minimal repro if possible)
- Affected components, versions, configuration, and environment details
- Impact assessment (what an attacker could do)
- Any proof-of-concept code or logs (redact secrets)

If the issue includes sensitive information (tokens, credentials, private URLs, etc.), **do not post secrets**. Remove/rotate them before submitting.

## What to expect

- We will triage security reports as part of normal development.
- Fixes will be prioritized alongside ongoing work based on severity and effort.
- We may ask for clarification or additional reproduction details.

## Security best practices for contributors

- Do not commit secrets (API keys, connection strings, certificates) to the repository.
- Prefer environment variables or a local secret store for development configuration.
- Keep dependencies updated and avoid introducing unnecessary new dependencies.
- If you introduce authentication/authorization or crypto-related changes, include tests.

## Disclosure

Please avoid public disclosure details (including exploit steps) until a fix is available.



================================================
FILE: _endpoint_stub.txt
================================================
    endpoint "GET" "/" Authenticated
    endpoint "POST" "/access/checkPermission" Authenticated
    endpoint "POST" "/access/grantRole" Authenticated
    endpoint "POST" "/access/listPathPermissions" Authenticated
    endpoint "POST" "/access/listRoleAssignments" Authenticated
    endpoint "GET" "/access/listRoles" Authenticated
    endpoint "POST" "/access/removePathPermission" Authenticated
    endpoint "POST" "/access/revokeRole" Authenticated
    endpoint "POST" "/access/upsertPathPermission" Authenticated
    endpoint "POST" "/admin/deleteAllFromCosmosDB" Authenticated
    endpoint "POST" "/admin/deleteAllRemindersFromCosmosDB" Authenticated
    endpoint "GET" "/auth/login" Authenticated
    endpoint "GET" "/auth/login/%s" Authenticated
    endpoint "GET" "/auth/logout" Authenticated
    endpoint "GET" "/auth/me" Authenticated
    endpoint "GET" "/auth/oidc/config" Authenticated
    endpoint "POST" "/auth/token/create" Authenticated
    endpoint "POST" "/auth/token/list" Authenticated
    endpoint "POST" "/auth/token/revoke" Authenticated
    endpoint "POST" "/branch/assign" Authenticated
    endpoint "POST" "/branch/checkpoint" Authenticated
    endpoint "POST" "/branch/commit" Authenticated
    endpoint "POST" "/branch/create" Authenticated
    endpoint "POST" "/branch/createExternal" Authenticated
    endpoint "POST" "/branch/delete" Authenticated
    endpoint "POST" "/branch/enableAssign" Authenticated
    endpoint "POST" "/branch/enableAutoRebase" Authenticated
    endpoint "POST" "/branch/enableCheckpoint" Authenticated
    endpoint "POST" "/branch/enableCommit" Authenticated
    endpoint "POST" "/branch/enableExternal" Authenticated
    endpoint "POST" "/branch/enablePromotion" Authenticated
    endpoint "POST" "/branch/enableSave" Authenticated
    endpoint "POST" "/branch/enableTag" Authenticated
    endpoint "POST" "/branch/get" Authenticated
    endpoint "POST" "/branch/getCheckpoints" Authenticated
    endpoint "POST" "/branch/getCommits" Authenticated
    endpoint "POST" "/branch/getDiffsForReferenceType" Authenticated
    endpoint "POST" "/branch/getEvents" Authenticated
    endpoint "POST" "/branch/getExternals" Authenticated
    endpoint "POST" "/branch/getParentBranch" Authenticated
    endpoint "POST" "/branch/getPromotions" Authenticated
    endpoint "POST" "/branch/getRecursiveSize" Authenticated
    endpoint "POST" "/branch/getReference" Authenticated
    endpoint "POST" "/branch/getReferences" Authenticated
    endpoint "POST" "/branch/getSaves" Authenticated
    endpoint "POST" "/branch/getTags" Authenticated
    endpoint "POST" "/branch/getVersion" Authenticated
    endpoint "POST" "/branch/listContents" Authenticated
    endpoint "POST" "/branch/promote" Authenticated
    endpoint "POST" "/branch/rebase" Authenticated
    endpoint "POST" "/branch/save" Authenticated
    endpoint "POST" "/branch/setPromotionMode" Authenticated
    endpoint "POST" "/branch/tag" Authenticated
    endpoint "POST" "/branch/updateParentBranch" Authenticated
    endpoint "POST" "/candidate/attestations" Authenticated
    endpoint "POST" "/candidate/cancel" Authenticated
    endpoint "POST" "/candidate/gate/rerun" Authenticated
    endpoint "POST" "/candidate/get" Authenticated
    endpoint "POST" "/candidate/required-actions" Authenticated
    endpoint "POST" "/candidate/retry" Authenticated
    endpoint "POST" "/diff/getDiff" Authenticated
    endpoint "POST" "/diff/getDiffBySha256Hash" Authenticated
    endpoint "POST" "/diff/populate" Authenticated
    endpoint "POST" "/directory/create" Authenticated
    endpoint "POST" "/directory/get" Authenticated
    endpoint "POST" "/directory/getByDirectoryIds" Authenticated
    endpoint "POST" "/directory/getBySha256Hash" Authenticated
    endpoint "POST" "/directory/getDirectoryVersionsRecursive" Authenticated
    endpoint "POST" "/directory/getZipFile" Authenticated
    endpoint "POST" "/directory/saveDirectoryVersions" Authenticated
    endpoint "GET" "/healthz" Authenticated
    endpoint "POST" "/organization/create" Authenticated
    endpoint "POST" "/organization/delete" Authenticated
    endpoint "POST" "/organization/get" Authenticated
    endpoint "POST" "/organization/listRepositories" Authenticated
    endpoint "POST" "/organization/setDescription" Authenticated
    endpoint "POST" "/organization/setName" Authenticated
    endpoint "POST" "/organization/setSearchVisibility" Authenticated
    endpoint "POST" "/organization/setType" Authenticated
    endpoint "POST" "/organization/undelete" Authenticated
    endpoint "POST" "/owner/create" Authenticated
    endpoint "POST" "/owner/delete" Authenticated
    endpoint "POST" "/owner/get" Authenticated
    endpoint "POST" "/owner/listOrganizations" Authenticated
    endpoint "POST" "/owner/setDescription" Authenticated
    endpoint "POST" "/owner/setName" Authenticated
    endpoint "POST" "/owner/setSearchVisibility" Authenticated
    endpoint "POST" "/owner/setType" Authenticated
    endpoint "POST" "/owner/undelete" Authenticated
    endpoint "POST" "/policy/acknowledge" Authenticated
    endpoint "POST" "/policy/current" Authenticated
    endpoint "POST" "/promotionGroup/addPromotion" Authenticated
    endpoint "POST" "/promotionGroup/block" Authenticated
    endpoint "POST" "/promotionGroup/complete" Authenticated
    endpoint "POST" "/promotionGroup/create" Authenticated
    endpoint "POST" "/promotionGroup/delete" Authenticated
    endpoint "POST" "/promotionGroup/get" Authenticated
    endpoint "POST" "/promotionGroup/getEvents" Authenticated
    endpoint "POST" "/promotionGroup/markReady" Authenticated
    endpoint "POST" "/promotionGroup/removePromotion" Authenticated
    endpoint "POST" "/promotionGroup/reorderPromotions" Authenticated
    endpoint "POST" "/promotionGroup/schedule" Authenticated
    endpoint "POST" "/promotionGroup/start" Authenticated
    endpoint "POST" "/queue/dequeue" Authenticated
    endpoint "POST" "/queue/enqueue" Authenticated
    endpoint "POST" "/queue/pause" Authenticated
    endpoint "POST" "/queue/resume" Authenticated
    endpoint "POST" "/queue/status" Authenticated
    endpoint "POST" "/reminder/create" Authenticated
    endpoint "POST" "/reminder/delete" Authenticated
    endpoint "POST" "/reminder/get" Authenticated
    endpoint "POST" "/reminder/list" Authenticated
    endpoint "POST" "/reminder/reschedule" Authenticated
    endpoint "POST" "/reminder/updateTime" Authenticated
    endpoint "POST" "/repository/create" Authenticated
    endpoint "POST" "/repository/delete" Authenticated
    endpoint "POST" "/repository/exists" Authenticated
    endpoint "POST" "/repository/get" Authenticated
    endpoint "POST" "/repository/getBranches" Authenticated
    endpoint "POST" "/repository/getBranchesByBranchId" Authenticated
    endpoint "POST" "/repository/getReferencesByReferenceId" Authenticated
    endpoint "POST" "/repository/isEmpty" Authenticated
    endpoint "POST" "/repository/setAllowsLargeFiles" Authenticated
    endpoint "POST" "/repository/setAnonymousAccess" Authenticated
    endpoint "POST" "/repository/setCheckpointDays" Authenticated
    endpoint "POST" "/repository/setConflictResolutionPolicy" Authenticated
    endpoint "POST" "/repository/setDefaultServerApiVersion" Authenticated
    endpoint "POST" "/repository/setDescription" Authenticated
    endpoint "POST" "/repository/setDiffCacheDays" Authenticated
    endpoint "POST" "/repository/setDirectoryVersionCacheDays" Authenticated
    endpoint "POST" "/repository/setLogicalDeleteDays" Authenticated
    endpoint "POST" "/repository/setName" Authenticated
    endpoint "POST" "/repository/setRecordSaves" Authenticated
    endpoint "POST" "/repository/setSaveDays" Authenticated
    endpoint "POST" "/repository/setStatus" Authenticated
    endpoint "POST" "/repository/setVisibility" Authenticated
    endpoint "POST" "/repository/undelete" Authenticated
    endpoint "POST" "/review/checkpoint" Authenticated
    endpoint "POST" "/review/deepen" Authenticated
    endpoint "POST" "/review/packet" Authenticated
    endpoint "POST" "/review/resolve" Authenticated
    endpoint "POST" "/storage/getDownloadUri" Authenticated
    endpoint "POST" "/storage/getUploadMetadataForFiles" Authenticated
    endpoint "POST" "/storage/getUploadUri" Authenticated
    endpoint "POST" "/work/create" Authenticated
    endpoint "POST" "/work/get" Authenticated
    endpoint "POST" "/work/link/promotion-group" Authenticated
    endpoint "POST" "/work/link/reference" Authenticated
    endpoint "POST" "/work/update" Authenticated


================================================
FILE: code_of_conduct.md
================================================
# Grace - Code of Conduct

![](./Assets/Orange3.svg)

## Culture

Culture is not a physical thing. You can't _touch_ culture. You can see it reflected in things like visual arts, clothing, books, code, UX, and so much else, but culture _itself_ is not an exterior object that can be seen and measured with some scientific apparatus.

Culture is _interior_; it lives _inside_ each member of a group. It's what it means when we say "we". It's first-person, plural. Culture is a set of stories or perspectives, shared by a group, that help to define both _how to behave_ within that group, and _how it feels_ to be part of that group.

I want to create a culture at Grace that lives up to its name. We all deserve it.

![](./Assets/Orange3.svg)

## The name of this project is _Grace_.

Be **graceful** in your interactions here.

Give **grace** to everyone participating with us.

Create something together that embodies **grace** in its design and form.

When in doubt, **_remember the name of the project._**

![](./Assets/Orange3.svg)


To be clear: if your behavior is more disruptive than constructive, you will be asked to leave.


================================================
FILE: docs/.markdownlint.jsonc
================================================
{
  // For the /docs directory, disable line length rule. We're writing prose.
  "MD013": {
    "enabled": false
  }
}


================================================
FILE: docs/AI Submissions.md
================================================
# AI Submissions for Grace

Grace welcomes AI-assisted contributions when they are transparent, reproducible, and technically strong.

## Why This Exists

Many projects ban AI-generated pull requests because low-quality submissions create review overhead.
Grace takes a different approach: AI assistance is welcomed, but only with clear disclosure, a high quality bar,
and standardized prompt outputs to make review as easy and as deterministic as possible.

## What We Invite

We want your contributions, both in issues and in pull requests. We just want you to run our prompts when you work on
those contributions to make review as streamlined as possible.

Please start with an issue for alignment on features and direction. Once the issue and new requirements are agreed on,
your contributions are welcomed. We don't want any "I have a huge change but didn't discuss it with anyone
first" contributions.

Use your preferred coding agent and provider to investigate issues, implement changes, and draft contribution materials.

For issues, use your agent to investigate the existing code and to develop an idea for a change. When you're ready
to submit the issue, you MUST use the [Grace issue summary](/prompts/Grace%20issue%20summary.md) prompt to create the
issue description.

For pull requests, when you're satisfied that your work is done and fully tested, you MUST use the [Grace pull request summary](/prompts/Grace%20pull%20request%20summary.md)
prompt with your coding agent to create the pull request description.

These standardized formats will help the maintainers of Grace keep up with everything you throw at us. (We hope.)

## Be Cool

Please treat the submission like professional engineering work product that you're proud of. ❤️

## Non-Negotiable Submission Rules

1. Use the latest and most powerful generally available reasoning model from your chosen provider at submission time.
   (i.e. Opus, not Sonnet; nothing with "mini" in the name) [^models]
2. Use reasoning at least equivalent to Codex and Claude's `high` (or stronger) to plan and implement the work.
3. Use the required Grace issue and pull request template prompts to have your agent automatically create the submission
   package.
   - Issue description prompt: [Grace issue summary.md](/prompts/Grace%20issue%20summary.md)
   - Pull request description prompt: [Grace pull request summary.md](/prompts/Grace%20pull%20request%20summary.md)
4. Include the prompts used to produce the final result.
5. Assume your output will be re-researched and reviewed by other LLMs and humans.

[^models]: I realize that this creates a barrier for AI submission that includes only those who can afford to run frontier models.
That's the minimum quality I need in the first half of 2026. I expect that by 2027 "Sonnet" and "Mini" level models will achieve a
similar capability level, and I will adjust these requirements when they're ready.

Write-ups should:

- Be clear.
- Be explicit.
- Be thorough.
- Prefer evidence over vague claims.

## Reasoning Level Mapping Guidance

Your provider may use different labels. Map your configuration to the closest equivalent that is at least Codex or Claude
`high`.

- OpenAI: `high` or `xhigh` reasoning.
- Anthropic: `high` or `max` effort.
- Google Gemini: thinking enabled with a high/maximum reasoning configuration.
- Other providers: choose the highest non-experimental reasoning mode generally available for production use.

If your provider exposes only vague labels, document exactly what you selected and why it is equivalent or stronger
than `high`.

## Quality Expectations

A compliant submission should make reviewer verification fast. The summary prompts are designed to let you create high-quality,
comprehensive submissions that validate that you've done the work correctly, and that communicate its value clearly to reviewers.

## Review and Enforcement

Submissions can be closed or requested for revision when:

1. Model/reasoning metadata is missing or unclear.
2. The run does not meet the latest-model or high-reasoning rule.
3. Prompt history is omitted.
4. The write-up is shallow, unverifiable, or inconsistent with the code.


================================================
FILE: docs/Authentication.md
================================================
# Authentication

<!-- markdownlint-configure-file {"MD013": {"line_length": 120}} -->

This document describes how to configure and use authentication for Grace during development.

Grace supports two authentication mechanisms:

1. **Auth0 (OIDC/JWT bearer tokens)** for interactive developer login (PKCE or Device Code) and
   machine-to-machine (client credentials).
1. **Grace Personal Access Tokens (PATs)** for automation and non-interactive usage.

> Important: Authentication proves “who you are.” Authorization (RBAC and path permissions) determines
> “what you can do.” PATs do **not** introduce a separate permission model; they authenticate a principal
> that is then authorized via Grace’s normal authorization system.

---

## Quickstart for contributors (recommended path)

1. **Set up Auth0** (one-time) using the instructions in **Auth0 tenant setup** below.
1. **Run Grace.Server** (typically via Aspire) with OIDC env vars configured.
1. **Point the Grace CLI at your server** by setting `GRACE_SERVER_URI`.

PowerShell:

```powershell
$env:GRACE_SERVER_URI="http://localhost:5000"
```

bash / zsh:

```bash
export GRACE_SERVER_URI="http://localhost:5000"
```

1. **Login via the CLI**:

   * `grace auth login` — Interactive login (tries PKCE first, then falls back to Device Code).
   * `grace auth whoami` — Verifies your identity against the running server.

---

## Authorization bootstrap (SystemAdmin seeding)

Grace supports a one-time bootstrap mechanism to seed the first SystemAdmin role assignment in a fresh environment.
This is required when you are standing up a new deployment that has no RBAC assignments yet.

### How it works

* Bootstrap runs **only** when the system-scope AccessControl actor first activates and has **no** existing assignments.
* It reads configured bootstrap principals and creates `SystemAdmin` role assignments at `Scope.System`.
* Bootstrap is **one-time** and will **never** overwrite or re-seed once any system-scope assignments exist.

### Configuration

Set one or both of these environment variables (semicolon-delimited list):

* `grace__authz__bootstrap__system_admin_users`
* `grace__authz__bootstrap__system_admin_groups`

### Important

* The values must be **principal IDs**, not emails or display names.
* For Auth0/OIDC, the user principal ID is taken from the `sub` claim (exposed as `grace_user_id`).
* You can confirm the user ID with `GET /auth/me` (`GraceUserId` in the response).

PowerShell:

```powershell
$env:grace__authz__bootstrap__system_admin_users="auth0|abc123"
```

bash / zsh:

```bash
export grace__authz__bootstrap__system_admin_users="auth0|abc123"
```

---

## Development without Auth0 (TestAuth)

For local development, you can skip Auth0 entirely by enabling the built-in TestAuth handler.
This is intended for **dev/test only**.

### Enable TestAuth

Set:

* `GRACE_TESTING=1` (also accepts `true` or `yes`)

When enabled, Grace authenticates requests using headers:

* `x-grace-user-id` — required; becomes the user principal ID (`grace_user_id`).
* `x-grace-claims` — optional; semicolon-delimited values mapped to `grace_claim`.

### Bootstrap as SystemAdmin without Auth0

1. Choose a local user ID (e.g., `dev-scott`).
1. Set bootstrap to that user ID:

   PowerShell:

   ```powershell
   $env:grace__authz__bootstrap__system_admin_users="dev-scott"
   ```

   bash / zsh:

   ```bash
   export grace__authz__bootstrap__system_admin_users="dev-scott"
   ```

1. Send requests with the `x-grace-user-id` header.

   PowerShell:

   ```powershell
   Invoke-RestMethod "http://localhost:5000/auth/me" -Headers @{ "x-grace-user-id" = "dev-scott" }
   ```

   bash / zsh:

   ```bash
   curl -H "x-grace-user-id: dev-scott" "http://localhost:5000/auth/me"
   ```

On first activation (with no existing assignments), Grace will seed `SystemAdmin` for `dev-scott`.
After that, bootstrap is a no-op.

---

## Auth0 tenant setup (development)

### What you need to create in Auth0

Grace needs these Auth0 resources:

1. **An Auth0 API (Resource Server)** for the Grace Server API

   * This provides the **Audience** value (the API Identifier).
1. **A Native Auth0 Application** for the Grace CLI (interactive login)

   * This provides the **CLI client ID**.
1. *(Optional)* **A Machine-to-Machine Auth0 Application** for CI/automation

   * This provides an **M2M client ID** and **M2M client secret**.

### Values you must capture from Auth0 (you will use these as env vars)

* **Tenant domain** (example: `my-tenant.us.auth0.com`)

  * Used to construct the **Authority**: `https://<tenant-domain>/`
* **API Identifier** (your “Audience”)

  * Example: `https://grace.local/api`
* **Native app Client ID** (Grace CLI interactive login)
* *(Optional)* **M2M app Client ID + Client Secret**

---

### Step 1: Create the Auth0 API (Resource Server)

Auth0 Dashboard steps:

1. Go to **Applications → APIs → Create API**.
1. Set:

   * **Name**: `Grace (Dev)` (or similar)
   * **Identifier** (this becomes the **Audience**): choose a stable string, e.g. `https://grace.local/api`
1. Enable **Allow Offline Access** on the API.

   * This is required so the CLI can receive refresh tokens (when requesting `offline_access`).
1. Save.

Record:

* API **Identifier** (Audience)
* Tenant Domain (Authority base)

---

### Step 2: Create the Auth0 Native Application (Grace CLI)

Auth0 Dashboard steps:

1. Go to **Applications → Applications → Create Application**.

1. Choose application type: **Native**.

1. In the application settings:

   * Ensure **Authorization Code** (PKCE) is enabled.
   * Ensure **Refresh Token** grant is enabled.
   * Ensure **Device Code** grant is enabled (so the CLI can use device flow on headless systems).

1. Configure **Allowed Callback URLs** to include the CLI callback URL:

   * Default Grace CLI callback URL:

     * `http://127.0.0.1:8391/callback`

   If you override the CLI redirect port (via `grace__auth__oidc__cli_redirect_port`), you must also
   update this callback URL accordingly.

1. Configure refresh token behavior (recommended for development):

   * Enable refresh token rotation (or equivalent Auth0 setting) and ensure refresh tokens are issued to
     the application.

Record:

* Native application **Client ID** (this is `grace__auth__oidc__cli_client_id`)

---

### Step 3 (optional): Create the Auth0 M2M Application (automation)

Auth0 Dashboard steps:

1. Create a new application of type **Machine to Machine**.
1. Authorize it to call your **Grace API (Resource Server)**.
1. Choose scopes if you’ve defined API scopes (Grace does not currently require Auth0 API scopes for
   authorization decisions, but your tenant policies may).
1. Record:

   * **Client ID** (`grace__auth__oidc__m2m_client_id`)
   * **Client Secret** (`grace__auth__oidc__m2m_client_secret`)

---

## Grace CLI authentication modes

The CLI can authenticate in multiple ways. The first matching mode “wins”:

1. **PAT mode** if `GRACE_TOKEN` is set (must be a Grace PAT, prefix `grace_pat_v1_`).
1. **Error** if `GRACE_TOKEN_FILE` is set (local token storage is intentionally disabled).
1. **M2M mode** if M2M env vars are set.
1. **Interactive mode** if you have logged in previously (token stored in OS secure store).

### Primary CLI commands

* `grace auth login [--auth pkce|device]`
  Interactive login to Auth0; stores access/refresh tokens in the OS secure store. If `--auth` is not
  specified, the CLI attempts PKCE and falls back to Device Code.

* `grace auth status`
  Shows whether the CLI currently has usable credentials (PAT, M2M, or interactive).

* `grace auth whoami`
  Calls the server and prints the authenticated identity information.

* `grace auth logout`
  Clears the cached interactive token from the secure store.

---

## Personal Access Tokens (PATs)

PATs are bearer tokens issued by Grace and validated by Grace. They are typically used for:

* CI jobs and automation
* running the CLI in non-interactive environments
* scripting against the Grace HTTP API

A PAT string looks like:

* `grace_pat_v1_<...>`

### Creating a PAT

You must already be authenticated (interactive Auth0 login, or an existing PAT, or M2M) to create a PAT.

#### CLI (recommended)

* `grace auth token create --name "<token-name>"`
  Creates a PAT with the server-default lifetime.

* `grace auth token create --name "<token-name>" --expires-in 30d`
  Creates a PAT that expires after the given duration. Supported suffixes: `s`, `m`, `h`, `d`.

* `grace auth token create --name "<token-name>" --no-expiry`
  Creates a non-expiring PAT **only if** the server allows it.

#### Notes

* The PAT value is a secret. Store it in a secret manager.
* Treat PATs like passwords; do not commit them into git.

### Using a PAT

Set the token in your environment:

PowerShell:

```powershell
$env:GRACE_TOKEN="grace_pat_v1_..."
```

bash / zsh:

```bash
export GRACE_TOKEN="grace_pat_v1_..."
```

Then run any CLI command as usual; authentication will use the PAT automatically.

If you are calling the HTTP API directly, use the standard Authorization header:

* `Authorization: Bearer grace_pat_v1_...`

### Listing and revoking PATs

* `grace auth token list`
  Lists active PATs for the current principal.

* `grace auth token list --all`
  Lists active + expired + revoked tokens.

* `grace auth token revoke <token-id>`
  Revokes a PAT by token ID (GUID). Revoked tokens are no longer accepted.

* `grace auth token status`
  Shows whether the current `GRACE_TOKEN` is present and parseable.

### How PAT “permissions” work in Grace

PATs do **not** have an independent “permission set” like some systems (GitHub fine-grained tokens, etc.). Instead:

* A PAT authenticates a principal (usually a user).
* Grace authorization is then evaluated normally:

  * **RBAC roles** assigned to the principal (user or group) at a scope
  * **Path permissions** keyed by “claims” and/or group membership

Important implementation detail:

* When a PAT is created, Grace snapshots the current principal’s `grace_claim` values and `grace_group_id`
  values into the token record on the server.
* If your group membership or claim set changes later, existing PATs will **not** automatically pick up
  those changes. Create a new PAT if you need a token that reflects updated claims/groups.

### Setting permissions for a PAT

Because a PAT’s authorization comes from the principal it authenticates, you “set PAT permissions” by
granting/revoking roles and path permissions for that principal.

Primary CLI commands for authorization management:

* `grace access grant-role ...`
  Grants a role to a principal at a scope (Owner/Organization/Repository/Branch/System).

* `grace access revoke-role ...`
  Revokes a role from a principal at a scope.

* `grace access list-role-assignments ...`
  Lists role assignments at a scope (optionally filtered by principal).

* `grace access upsert-path-permission ...`
  Sets or updates a path permission entry in a repository.

* `grace access remove-path-permission ...`
  Removes a path permission entry.

* `grace access list-path-permissions ...`
  Lists path permissions, optionally scoped to a path prefix.

* `grace access check ...`
  Asks the server “would this principal be allowed to do operation X on resource Y?”

> For detailed role IDs and operations, use `grace access list-roles` and consult the authorization types
> in `Grace.Types.Authorization`.

---

## Environment variables

This section documents auth-related environment variables used by Grace Server and the Grace CLI.

### Grace CLI (always relevant)

* `GRACE_SERVER_URI` (required for CLI)
  **No default.** Must point to the running Grace server base URI.
  Source: Aspire dashboard output, local run output, or your deployment URL.

Example value:

* `http://localhost:5000`

PowerShell:

```powershell
$env:GRACE_SERVER_URI="http://localhost:5000"
```

bash / zsh:

```bash
export GRACE_SERVER_URI="http://localhost:5000"
```

---

### Grace Server OIDC configuration (Auth0/JWT)

These variables enable Auth0 JWT authentication on the server.

* `grace__auth__oidc__authority` (optional, enables OIDC when set)
  **No default.**
  Source: your Auth0 tenant domain. Use `https://<tenant-domain>/`.

* `grace__auth__oidc__audience` (required if `...__authority` is set)
  **No default.**
  Source: Auth0 API Identifier (Resource Server “Identifier”).

Recommended (for CLI auto-config):

* `grace__auth__oidc__cli_client_id` (optional for server auth; required for `/auth/oidc/config`)
  **No default.**
  Source: Auth0 Native app Client ID.

> If `grace__auth__oidc__authority` and `grace__auth__oidc__audience` are not set, the server will fall
> back to PAT-only authentication.

---

### Grace CLI interactive OIDC configuration (Auth0 login)

The Grace CLI can authenticate interactively using Auth0 (OIDC). There are two ways to supply the required
OIDC settings:

1. **Recommended:** have the CLI fetch OIDC settings from the Grace Server.
1. **Advanced:** configure OIDC settings directly on the CLI via environment variables.

---

#### Recommended: CLI auto-configuration from the server

If you set only:

* `GRACE_SERVER_URI` — Base URI of the running Grace Server (example: `http://localhost:5000`)

…then the CLI can fetch the OIDC configuration automatically by calling:

* `GET /auth/oidc/config` — Returns the server’s OIDC settings needed for interactive login.

This works **only if** the server is configured with OIDC and has the values needed to publish them (see
server env vars below).

##### How to use (server auto-configuration)

1. Start the server with OIDC enabled (Authority + Audience, and preferably the CLI client ID).

1. Set the server URI:

   PowerShell:

   ```powershell
   $env:GRACE_SERVER_URI="http://localhost:5000"
   ```

   bash / zsh:

   ```bash
   export GRACE_SERVER_URI="http://localhost:5000"
   ```

1. Login:

   * `grace auth login` — Interactive Auth0 login (tries PKCE, then device flow).

1. Verify:

   * `grace auth whoami` — Calls the server and prints the authenticated identity.

##### Server-side requirements for auto-configuration

For `GET /auth/oidc/config` to return useful values, the server must be configured with:

* `grace__auth__oidc__authority` — `https://<tenant-domain>/`
* `grace__auth__oidc__audience` — Auth0 API Identifier
* `grace__auth__oidc__cli_client_id` — Auth0 Native app Client ID (recommended)

If the server is missing `grace__auth__oidc__cli_client_id`, the CLI may still be able to login if you
supply the client ID locally (see Advanced).

---

#### Advanced: set OIDC settings on the client

If you cannot use server auto-configuration (for example, you are testing against an endpoint that does
not expose `/auth/oidc/config`), you can configure the CLI directly via environment variables.

##### Required

* `grace__auth__oidc__authority`
  No default. Source: Auth0 tenant domain.
  Format: `https://<tenant-domain>/`

* `grace__auth__oidc__audience`
  No default. Source: Auth0 API Identifier (Resource Server “Identifier”).

* `grace__auth__oidc__cli_client_id`
  No default. Source: Auth0 Native app Client ID.

##### Optional (recommended defaults)

* `grace__auth__oidc__cli_redirect_port`
  Default: `8391`
  If you change this, you must also update the Auth0 Native app callback URL to:
  `http://127.0.0.1:<port>/callback`

* `grace__auth__oidc__cli_scopes`
  Default: `openid profile email offline_access`
  `offline_access` is required to receive refresh tokens.

##### How to use (client configuration)

1. Set environment variables.

   PowerShell:

   ```powershell
   $env:GRACE_SERVER_URI="http://localhost:5000"
   $env:grace__auth__oidc__authority="https://<tenant-domain>/"
   $env:grace__auth__oidc__audience="https://grace.local/api"
   $env:grace__auth__oidc__cli_client_id="<native-client-id>"
   ```

   bash / zsh:

   ```bash
   export GRACE_SERVER_URI="http://localhost:5000"
   export grace__auth__oidc__authority="https://<tenant-domain>/"
   export grace__auth__oidc__audience="https://grace.local/api"
   export grace__auth__oidc__cli_client_id="<native-client-id>"
   ```

1. Login:

   * `grace auth login` — Interactive Auth0 login (tries PKCE, then device flow).

1. Verify:

   * `grace auth whoami` — Calls the server and prints the authenticated identity.

---

### Grace CLI machine-to-machine (M2M) configuration

If you set these env vars, the CLI will use Auth0 client credentials to obtain an access token.

* `grace__auth__oidc__authority`
  **No default.** Source: Auth0 tenant domain.

* `grace__auth__oidc__audience`
  **No default.** Source: Auth0 API Identifier.

* `grace__auth__oidc__m2m_client_id`
  **No default.** Source: Auth0 M2M application Client ID.

* `grace__auth__oidc__m2m_client_secret`
  **No default.** Source: Auth0 M2M application Client Secret.

Optional:

* `grace__auth__oidc__m2m_scopes`
  Default: empty
  Space-separated list of scopes to request (if your tenant requires/uses them).

PowerShell:

```powershell
$env:grace__auth__oidc__authority="https://<tenant-domain>/"
$env:grace__auth__oidc__audience="https://grace.local/api"
$env:grace__auth__oidc__m2m_client_id="<m2m-client-id>"
$env:grace__auth__oidc__m2m_client_secret="<m2m-client-secret>"
# Optional:
$env:grace__auth__oidc__m2m_scopes="read:foo write:bar"
```

bash / zsh:

```bash
export grace__auth__oidc__authority="https://<tenant-domain>/"
export grace__auth__oidc__audience="https://grace.local/api"
export grace__auth__oidc__m2m_client_id="<m2m-client-id>"
export grace__auth__oidc__m2m_client_secret="<m2m-client-secret>"
# Optional:
export grace__auth__oidc__m2m_scopes="read:foo write:bar"
```

---

### Grace CLI PAT configuration

* `GRACE_TOKEN` (optional)
  **No default.**
  Source: output from `grace auth token create`.
  Must be a Grace PAT (prefix `grace_pat_v1_`). If set, this overrides interactive login and M2M.

* `GRACE_TOKEN_FILE`
  **Not supported.** Local plaintext token file storage is intentionally disabled.

---

### Grace Server PAT policy controls

These affect how the server handles PAT creation requests:

* `grace__auth__pat__default_lifetime_days`
  Default: `90`

* `grace__auth__pat__max_lifetime_days`
  Default: `365`

* `grace__auth__pat__allow_no_expiry`
  Default: `false`

---

## Getting Auth0 values automatically (CLI + APIs)

If you already have an Auth0 tenant configured, the **Auth0 CLI** can be used to find the exact values
needed for Grace without clicking through the dashboard.

### Auth0 CLI login

* `auth0 login`
  Authenticates the Auth0 CLI for interactive use (or with client credentials for CI).

If you need additional Management API scopes, re-run login with scopes, e.g.:

* `auth0 login --scopes "read:client_grants,create:client_grants"`

### Find tenant information

* `auth0 tenants list --json`
  Lists tenants accessible to your Auth0 CLI session.

* `auth0 tenant-settings show --json`
  Shows tenant settings (useful to confirm you’re operating on the expected tenant).

### Find the Grace API audience (API Identifier)

* `auth0 apis list --json`
  Lists APIs (Resource Servers). Find your Grace API and read its `identifier`.

* `auth0 apis show <api-id|api-audience> --json`
  Shows details for a specific API.

### Find the CLI Client ID (Native app) and M2M credentials

* `auth0 apps list --json`
  Lists applications.

* `auth0 apps show <app-id> --json`
  Shows app details (including `client_id`).

* `auth0 apps show <app-id> --reveal-secrets --json`
  Shows app details **including secrets** (use only for M2M apps, and handle output carefully).

### Make raw Management API calls (advanced)

* `auth0 api get "tenants/settings"`
  Makes an authenticated request to the Auth0 Management API and prints JSON.

This is useful if you need endpoints not exposed by a dedicated `auth0 <noun> <verb>` command.

### Creating Auth0 resources via the Auth0 CLI (optional)

You can create Auth0 resources non-interactively.

* `auth0 apis create ...`
  Creates an API (Resource Server). You can set identifier (audience), token lifetime, and offline access.

* `auth0 apps create ...`
  Creates an application (Native / M2M / etc).

* `auth0 apps update ...`
  Updates an application (callbacks, grant types, refresh token config, etc).

> If you prefer the dashboard, you can ignore this section entirely.

---

## Troubleshooting

### `grace auth login` fails and mentions refresh tokens

Ensure:

* the Auth0 API has **Allow Offline Access** enabled
* the Auth0 Native app allows refresh tokens and is configured to issue them
* `grace__auth__oidc__cli_scopes` includes `offline_access`

### `grace status` returns `Unauthorized` and Grace.Server logs look empty

This usually means the CLI sent branch status requests without a usable token.

Common causes:

* No interactive token is cached yet.
* `GRACE_TOKEN` is not set (or is invalid).
* You are expecting endpoint handler logs, but the request is rejected by authentication middleware first.

Quick checks:

PowerShell:

```powershell
grace auth status --output Verbose
grace auth token status --output Verbose
grace status --output Verbose
```

bash / zsh:

```bash
grace auth status --output Verbose
grace auth token status --output Verbose
grace status --output Verbose
```

If `GRACE_TOKEN` is false and interactive token is false, authenticate first:

PowerShell:

```powershell
grace auth login --auth device
# or
grace auth login --auth pkce
```

bash / zsh:

```bash
grace auth login --auth device
# or
grace auth login --auth pkce
```

If you are using a PAT:

PowerShell:

```powershell
$env:GRACE_TOKEN="grace_pat_v1_..."
grace auth token status --output Verbose
```

bash / zsh:

```bash
export GRACE_TOKEN="grace_pat_v1_..."
grace auth token status --output Verbose
```

Why logs may look empty:

* `grace status` maps to `branch status`, which calls `/branch/Get` and `/branch/GetParentBranch`.
* Those routes require authentication and are rejected with `401` before business handlers run.
* You might see little or no endpoint-level logging for these failures.

Where to look for proof of `401`:

* W3C request logs under `%TEMP%\Grace.Server.Logs` (Windows) show request-level status codes.
* Look for entries like `POST /branch/Get` and `POST /branch/GetParentBranch` with `401`.

Also note:

* `--output` values are case-sensitive in current CLI behavior. Use `Verbose`, not `verbose`.
* During development, TestAuth may be available. See this file for setup details:
  `docs/Authentication.md` -> **Development without Auth0 (TestAuth)**.

### You can see `SystemAdmin` in Cosmos, but `grace access check` says denied

If `grace auth whoami` shows your expected `GraceUserId`, but:

* `grace access check --operation SystemAdmin --resource system --output Json`
  returns `Denied: missing permission SystemAdmin.`,

and you can also see a `SystemAdmin` assignment document in Cosmos, the most common cause is a **runtime context mismatch**.

Typical mismatch causes:

* The running server is using a different Orleans `serviceid` than the one that wrote the document.
* You inspected a different Cosmos database or container than the running server is using.
* The AccessControl grain loaded state before manual Cosmos edits (stale in-memory state until restart).

Why this happens:

* System-scope role assignments are read from the AccessControl actor state keyed by scope and Orleans identity.
* A document such as `grace-dev__accesscontrolactor_system` applies only to the matching Orleans service context.
* If the active server context differs, authorization reads a different actor record.

What to verify first:

PowerShell:

```powershell
grace auth whoami --output Json
grace access check --operation SystemAdmin --resource system --output Json
```

bash / zsh:

```bash
grace auth whoami --output Json
grace access check --operation SystemAdmin --resource system --output Json
```

Then verify server configuration:

* `GRACE_SERVER_URI` points to the server you think you are testing.
* The running server's Orleans `serviceid` matches the service prefix of the AccessControl actor document.
* The running server's Cosmos database/container match the place where you inspected the document.

Recommended recovery steps:

1. Restart `Grace.Server` (or your Aspire app host) to clear any stale grain state.
2. Re-run the two checks above.
3. Re-open Cosmos and confirm the `system` AccessControl actor document for the active service context
   contains your user principal.
4. If needed, use a current `SystemAdmin` principal to grant your role again via `grace access grant-role`.

### Headless environments / CI

Use:

* M2M auth (client credentials env vars), or
* PATs (`GRACE_TOKEN`), or
* `grace auth login --auth device` if interactive login is still acceptable.


================================================
FILE: docs/Branching strategy.md
================================================
![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

# Simplified branching strategy

> Quick note: Grace doesn't have a `merge` gesture; it has a `promote` gesture. The idea is that you're not merging a changeset into a parent branch, you're saying "The parent branch is changing its current version to point to the version in the child branch."

Grace's default branching strategy is meant to be simple, and to help surface merge conflicts as early as possible. It's called "single-step". If we're right, it's all you need to successfully run a project.

Branching strategy is the thing about Grace that's most different from other version control systems. Because it's so different, it's worth going over it in some detail.

In single-step branching, each child branch can be promoted only to its parent branch, and must be based on the most-recent version in the parent before being allowed to be promoted.

In other words: if I'm based on my parent branch, which means that my code includes everything in the parent branch, up to right now, then any changes in my branch are exactly equivalent to the changeset that we might imagine is being applied to the parent branch.

`grace watch` helps with single-step branching by auto-rebasing your branch when a parent branch's version changes. The vast majority of the time you'll be based on the most-recent parent version without having to do anything manually, and without noticing that anything happened. (And, yes, you can turn off auto-rebase if you need to.) (..but you should try it first.)

Here's a simple example:

![](https://gracevcsdevelopment.blob.core.windows.net/static/Green.svg)

Alice and Bob are developers in the same repo, and have individual branches, named `Alice` and `Bob`, parented by `main`.

When they do saves, checkpoints, or commits, those happen on their own branches.

When they promote, they promote only to `main`.

Grace keeps track of which version of their parent branch they're (re-)based on.

```mermaid
flowchart BT
    Alice[Alice, based on main/ed3f4280]-->main[main/ed3f4280]
    Bob[Bob, based on main/ed3f4280]-->main[main/ed3f4280]
```

|Branch|Current version|Based on|
|-|-:|-:|
|Main|`ed3f4280`|\<root\>|
|Alice|`425684d8`|`ed3f4280`|
|Bob|`9c2afa14`|`ed3f4280`||Main|ed3f4280|ed3f4280|

![](https://gracevcsdevelopment.blob.core.windows.net/static/Green.svg)

Let's imagine that Alice makes one last update to her branch, has an updated SHA-256 value for it, and (assuming the PR is approved) promotes it to `main`.

```mermaid
flowchart BT
    Alice[Alice, based on main/ed3f4280]-->|promoting 56d626f4|main[main/ed3f4280]
    Bob[Bob, based on main/ed3f4280]-->main[main/ed3f4280]
```

|Branch|Current version|Based on|
|-|-:|-:|
|Main|`ed3f4280`|\<root\>|
|Alice|`56d626f4`|`ed3f4280`|
|Bob|`9c2afa14`|`ed3f4280`|

![](https://gracevcsdevelopment.blob.core.windows.net/static/Green.svg)

Everything goes well, Grace completes the promotion, and `main` is updated to point to the 56d626f4 version.

`Alice`, also pointing to 56d626f4, marks itself as based on it.

`Bob` is no longer based on the latest version in `main`, and is therefore ineligible to promote to `main`.

```mermaid
flowchart BT
    Alice[Alice, based on main/56d626f4]-->main[main/56d626f4]
    Bob[Bob, based on main/ed3f4280]-->mainold[main/ed3f4280]
```

|Branch|Current version|Based on|
|-|-:|-:|
|Main|`56d626f4`|\<root\>|
|Alice|`56d626f4`|`56d626f4`|
|Bob|`9c2afa14`|`ed3f4280`|

![](https://gracevcsdevelopment.blob.core.windows.net/static/Green.svg)

Fortunately, Bob is running `grace watch`, so seconds later `Bob` is auto-rebased on that latest parent version in `main`.

Let's assume there are no conflicts.[^conflict] The files that were updated in `main` are different that then ones updated in `Bob`. The new file versions from `main` are copied into place in the working directory.

This new version of `Bob`, which includes whatever was already changed in the branch, and whatever changed in the rebase, has a new SHA-256 value, and is automatically uploaded as a save.

`Bob` is once again eligible to promote code to `main`.

```mermaid
flowchart BT
    Alice[Alice, based on main/56d626f4]-->main[main/56d626f4]
    Bob[Bob, now based on main/56d626f4]<-->|rebase on 56d626f4|main[main/56d626f4]
```

|Branch|Current version|Based on|
|-|-:|-:|
|Main|`56d626f4`|\<root\>|
|Alice|`56d626f4`|`56d626f4`|
|Bob|`21519a1b`|`56d626f4`|

![](https://gracevcsdevelopment.blob.core.windows.net/static/Green.svg)

[^conflict]: If there are conflicts, Grace will have a native conflict-resolution UI, as well as a way to navigate it through the CLI.


================================================
FILE: docs/Continuous review.md
================================================
# Continuous review

Continuous review tracks promotion-set candidates, evaluates gates, and records
review artifacts in a deterministic and auditable flow.

## Canonical workflow model

- Use `grace queue ...` for promotion-set queue operations.
- Use `grace candidate ...` for candidate-first reviewer operations:
  `get`, `required-actions`, `attestations`, `retry`, `cancel`, `gate rerun`.
- Use `grace review ...` for promotion-set scoped reviewer actions:
  `open`, `checkpoint`, `resolve`.
- Use `grace review report ...` for candidate-scoped report output:
  `show`, `export`.

## Current API surface

### Queue endpoints

- `POST /queue/status`
- `POST /queue/enqueue`
- `POST /queue/pause`
- `POST /queue/resume`
- `POST /queue/dequeue`

### Candidate and report endpoints

- `POST /review/candidate/get`
- `POST /review/candidate/retry`
- `POST /review/candidate/cancel`
- `POST /review/candidate/required-actions`
- `POST /review/candidate/attestations`
- `POST /review/candidate/gate-rerun`
- `POST /review/report/get`

### Review endpoints

- `POST /review/notes`
- `POST /review/checkpoint`
- `POST /review/resolve`
- `POST /review/deepen` (stub)

### Policy endpoints

- `POST /policy/current`
- `POST /policy/acknowledge`

## CLI examples

### Queue management

PowerShell:

```powershell
./grace queue enqueue `
  --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000 `
  --branch main `
  --policy-snapshot-id e3b0c44298fc1c149afbf4c8996fb924 `
  --work 42

./grace queue status --branch main
./grace queue pause --branch main
./grace queue resume --branch main
./grace queue dequeue --branch main --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000
```

bash / zsh:

```bash
./grace queue enqueue \
  --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000 \
  --branch main \
  --policy-snapshot-id e3b0c44298fc1c149afbf4c8996fb924 \
  --work 42

./grace queue status --branch main
./grace queue pause --branch main
./grace queue resume --branch main
./grace queue dequeue --branch main --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000
```

`--work` accepts either a GUID `WorkItemId` or a numeric `WorkItemNumber`.

### Candidate-first operations

PowerShell:

```powershell
./grace candidate get --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c
./grace candidate required-actions --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c
./grace candidate attestations --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c
./grace candidate retry --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c
./grace candidate gate rerun --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c --gate policy
```

bash / zsh:

```bash
./grace candidate get --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c
./grace candidate required-actions --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c
./grace candidate attestations --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c
./grace candidate retry --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c
./grace candidate gate rerun --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c --gate policy
```

### Promotion-set review actions

PowerShell:

```powershell
./grace review open --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000

./grace review checkpoint `
  --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000 `
  --reference-id f12a0d31-0d5a-4a5f-a5a7-3d2c3a9f5b2c

./grace review resolve `
  --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000 `
  --finding-id 6e58b4de-7f3b-4a2b-9a6f-111111111111 `
  --approve `
  --note "Reviewed and acceptable."
```

bash / zsh:

```bash
./grace review open --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000

./grace review checkpoint \
  --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000 \
  --reference-id f12a0d31-0d5a-4a5f-a5a7-3d2c3a9f5b2c

./grace review resolve \
  --promotion-set 3d5c4d9a-0123-4567-89ab-987654321000 \
  --finding-id 6e58b4de-7f3b-4a2b-9a6f-111111111111 \
  --approve \
  --note "Reviewed and acceptable."
```

### Report-first review output

PowerShell:

```powershell
./grace review report show --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c

./grace review report export `
  --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c `
  --format markdown `
  --output-file .\review-report.md
```

bash / zsh:

```bash
./grace review report show --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c

./grace review report export \
  --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c \
  --format markdown \
  --output-file ./review-report.md
```

### Source attribution in history

Use the global `--source` option to tag and filter automation activity.

PowerShell:

```powershell
./grace history show --source codex
./grace history search workitem --source codex
```

bash / zsh:

```bash
./grace history show --source codex
./grace history search workitem --source codex
```

You can also set the environment fallback:

PowerShell:

```powershell
$env:GRACE_SOURCE="codex"
./grace history show
```

bash / zsh:

```bash
export GRACE_SOURCE="codex"
./grace history show
```

## Current limitations and stubs

- `grace review inbox` is still a CLI stub.
- `grace review deepen` is still a CLI and server stub.
- Queue processing and automatic candidate state transitions are not fully
  orchestrated server-side; external automation is expected.
- Gate implementations are still partial for some gate types.


================================================
FILE: docs/Data types in Grace.md
================================================
# Data types in Grace

Grace uses a fairly simple data structure to keep track of everything. It's more robust than Git's, for sure, but it's as simple as I could make it.

In this document, first, you'll find an Entity Relationship Diagram (ERD) showing the most relevant types.

After the diagram, you'll find descriptions of each data type. You can skip directly to the data type you're interested in by clicking the corresponding link below:

- [Owner and Organization](#owner-and-organization-ie-multitenancy)
- [Repository](#repository)
- [Branch](#branch)
- [DirectoryVersion](#directoryversion)
- [Reference](#reference)
- [FileVersion](#fileversion)

I'm sure the types will evolve a bit as we move towards a 1.0 release, but the overall structure should be stable now.

After those descriptions, at the bottom of this document, you'll find a [detailed entity relationship diagram](#detailed-entity-relationship-diagram). This ERD is incomplete, and there are, of course, many other data types in Grace. It's meant to illustrate the most interesting parts, to help you understand the structure of a repository and its contents. Please refer to it as you read the explanations of each type.

## Entity Relationship Diagram

The diagram below shows the most important data types in Grace, and how they relate to each other. A [more-detailed ERD](#detailed-entity-relationship-diagram) is available at the bottom of this document.

```mermaid
erDiagram
    Owner ||--|{ Organization : "has 1:N"
    Organization ||--|{ Repository : "has 1:N"
    Repository ||--|{ Branch : "has 1:N"
    Branch ||--|{ Reference : "has 0:N"
    Repository ||--|{ DirectoryVersion : "has 1:N"
    Reference ||--|| DirectoryVersion : "refers to exactly 1"
    DirectoryVersion ||--|{ FileVersion : "has 0:N"
```

## Owner and Organization; i.e. Multitenancy

Grace has a lightweight form of multitenancy built-in. This structure is meant to help large version control hosting platforms to integrate Grace with their existing customer and identity systems.

I've specifically chosen to do have a two-level Owner / Organization structure based on my experience at GitHub. GitHub started with the construct of an Organization, and in recent years has been adding an "Enterprise" construct above Organizations, to allow large companies to have multiple Organizations managed under one structure. Seeing the importance of that feature set to large companies made it an easy decision to just start with a two-level structure.

It's not my intention for Grace to replace the identity / organization system for any hoster, and that's why there really isn't much in these data types. They're meant to be "hooks" that a hoster can refer to from their identity systems so they can implement whatever management features they need to safely serve Grace repositories.

Owner and Organization are the least-used of the data types here. They get created relatively infrequently, they get updated even less frequently, and they get deleted not much at all.

### What about personal accounts?

For individual users - like personal user accounts on GitHub that don't belong to any organization - Grace will have one Owner and one Organization that is just for that user, and all user-owned repositories would sit under that Organization.

There's nothing stopping an individual user from having multiple Organizations (unless the hoster prevents it). There's no performance difference either way.

## Repository

Now we get to the version control part.

Repository is where Grace keeps settings that apply to the entire repository, that apply to each branch by default, and that apply to References and DirectoryVersions in the repository.

Some examples:

- RepositoryType - Is the repository public or private?
- SearchVisibility - Should the contents of this repository be visible in search?
- Timings for deleting various entities -
  - LogicalDeleteDays - How long should a deleted object be kept before being physically deleted?
  - SaveDays - How long should Save References be kept?
  - CheckpointDays - How long should Checkpoint References be kept?
  - DirectoryVersionCacheDays - How long should the memoized contents of the entire directory tree under a DirectoryVersion be kept?
  - DiffCacheDays - How long should the memoized results of a Diff between two DirectoryVersions be kept?
- RecordSaves - Should Auto-save be turned on for this repository?

In general, once a Repository is created and the settings adjusted to taste, the Repository record will be updated very infrequently.

## Branch

Branch is where branches in a repository are defined. It just holds settings that apply to the Branch.

The most important settings there are:

- ParentBranchId - Which branch is the parent of this branch?
- \<_Reference_\>Enabled - These control which kinds of References are allowed on the Branch
  - PromotionEnabled
  - CommitEnabled
  - CheckpointEnabled
  - SaveEnabled
  - TagEnabled
  - ExternalEnabled

I'm sure there will be more settings here as we get to v1.0.

Branches are created and deleted frequently, of course, but they're updated pretty infrequently.

That might seem weird if you're used to Git. In Grace, when you do things like `grace checkpoint` or `grace commit` you're not updating the status of a Branch; you're creating a new Reference _in_ that branch. Nothing in the Branch itself changes.

## DirectoryVersion

DirectoryVersion holds the data for a specific version of a directory anywhere in a repo. Every time a file in a directory changes, a new DirectoryVersion is created that holds the new state of the directory. If the contents of a subdirectory change, that directory will get a new DirectoryVersion, and so will the next directory up the tree, until we reach the root of the repository.

In other words, DirectoryVersion is how we capture each unique state in a repository.

One interesting thing here is that, like the other entities here, Grace uses a Guid for the primary key DirectoryVersionId, and does not use the Sha256Hash as the unique key (even though it always will be unique). My reason for choosing to have an artificial key instead of just using the Sha256Hash is the challenge that Git has had, and is having, migrating to SHA-256, given how deeply embedded SHA-1 is in the naming of objects in Git. It seems best to keep Sha256Hash as a data field, and not as a key, to make it easier to change the hash algorithm in the future.

Also, DirectoryVersion has the RepositoryId it belongs to, but does not keep a BranchId. This is because a unique version of the Repository, i.e. a DirectoryVersion, can be pointed to from multiple References and from multiple Branches.

So, DirectoryVersion contains:

- DirectoryVersionId - This is a Guid that uniquely identifies each DirectoryVersion.
- RepositoryId - not BranchId
- Sha256Hash - Computed over the contents of the directory; the algorithms for computing the Sha256Hash of a [file](https://github.com/ScottArbeit/Grace/blob/337ed395b7f5d033ceb9d178b4fd9442fa383ee5/src/Grace.Shared/Services.Shared.fs#L53) and a [directory](https://github.com/ScottArbeit/Grace/blob/337ed395b7f5d033ceb9d178b4fd9442fa383ee5/src/Grace.Shared/Services.Shared.fs#L92) are in [Services.Shared.fs](https://github.com/ScottArbeit/Grace/blob/main/src/Grace.Shared/Services.Shared.fs).
- RelativePath - no leading '/'; for instance `src/foo/bar.fs`
- Directories - a list of DirectoryVersionId's that refer to the sub-DirectoryVersions.
- Files - a list of FileVersions, one for each not-ignored file in the directory
- Size - int64

DirectoryVersions are created and deleted frequently, as References are created and deleted.

### RootDirectoryVersion

Because it's such an important construct, in Grace's code you'll see `RootDirectoryVersion` a lot. This is a DirectoryVersion with the path '.', which is the [definition of "root directory"](https://github.com/ScottArbeit/Grace/blob/337ed395b7f5d033ceb9d178b4fd9442fa383ee5/src/Grace.Shared/Constants.Shared.fs#L173-L174) in Grace. Because the RootDirectoryVersion sits at the top of the directory tree, we point to it in a Reference, rather than any sub-DirectoryVersion, as representing a unique version of the repository.

## Reference

In Grace, a Reference is how we mark specific RootDirectoryVersions as being interesting in one way or another.

References have a ReferenceType that indicates what kind it is, so there's no such thing as a Commit entity or a Save entity. They're all just References.

The interesting parts of a Reference are:

- ReferenceId - This is a Guid that uniquely identifies each Reference.
- BranchId - The Branch that this Reference is in. A Reference can only be in one Branch.
- DirectoryVersionId - The RootDirectoryVersion that this Reference points to.
- Sha256Hash - The Sha256Hash of the DirectoryVersionId that this Reference points to. Denormalized here for performance reasons.
- ReferenceType - What kind of Reference is this?
  - Promotion - This is a Reference that was created by promoting a Commit reference from a child branch to this branch.
  - Commit - Commits are candidates for promotion.
  - Checkpoint - This is for you to mark a specific version of the repository as being interesting to you. In Git, this is what you'd think of as an intermediate commit as you complete your work.
  - Save - These are automatically created by Grace on every save-on-disk, if Auto-Save is turned on.
  - Tag - This is a Reference that was created by tagging a Reference.
  - External - This is a Reference that was created by an external system, like a CI system.
  - Rebase - This is the Reference that gets created when a branch is Rebased on the latest Promotion in its parent branch
- ReferenceType - The attached to the Reference.
- Links - This is a way to link this Reference to another in some relationship.

References and DirectoryVersions are where the action happens. New References and DirectoryVersions are being created with every save-on-disk (if you have Auto-Save turned on, which you should), and with every checkpoint / commit / promote / tag / external.

The ratio of new-DirectoryVersions-to-new-References is directly proportional to how deep in the directory tree the updated files are. For every directory level, a new DirectoryVersion will be created. For example, if I update a file called `src/web/js/lib/blah.js` and hit save, that will create one Save Reference, and five new DirectoryVersions - one for the root, and one each for each directory in the path.

Saves have short lifetimes, and checkpoints (by default) have longer, but finite, lifetimes, and they both get deleted at some point. Any DirectoryVersions that are unique to those references, and any FileVersions in object storage that only appear in those references, get deleted when the Reference is deleted.

Also, of course, every time a Branch is deleted, all References in that Branch get deleted. And all DirectoryVersions unique to those References get deleted. Etc.

It's completely normal in Grace for References to be deleted. Happens all the time.

## FileVersion

The FileVersion contains the metadata for a file in a DirectoryVersion. It's the metadata for the file, not the file itself.

The file itself is stored in object storage, and the FileVersion has a BlobUri that points to it.

The interesting parts of a FileVersion are:

- RepositoryId - The Repository that this FileVersion is in.
- RelativePath - The path of the file, relative to the Repository root.
- Sha256Hash - The Sha256Hash of the file.
- IsBinary - Is the file binary?
- Size - The size of the file (int64).
- BlobUri - The URI of the file in object storage.

## Detailed Entity Relationship Diagram

The diagram below shows the most important data types in Grace, and how they relate to each other. Not every field in each data type is shown - feel free to check out [Types.Shared.fs](https://github.com/ScottArbeit/Grace/blob/main/src/Grace.Shared/Types.Shared.fs) and [Dto.Shared.fs](https://github.com/ScottArbeit/Grace/blob/main/src/Grace.Shared/Dto/Dto.Shared.fs) to see the full data types - but this should give you a good idea of how the data is structured.

```mermaid
erDiagram
    Owner ||--|{ Organization : "has 1:N"
    Owner {
        OwnerId Guid
        OwnerName string
        OwnerType OwnerType
        SearchVisibility SearchVisibility
    }
    Organization ||--|{ Repository : "has 1:N"
    Organization {
        OrganizationId Guid
        OrganizationName string
        OwnerId Guid
        OrganizationType OrganizationType
        SearchVisibility SearchVisibility
    }
    Repository ||--|{ Branch : "has 1:N"
    Repository {
        RepositoryId Guid
        RepositoryName string
        OwnerId Guid
        OrganizationId Guid
        RepositoryType RepositoryType
        RepositoryStatus RepositoryStatus
        DefaultServerApiVersion string
        DefaultBranchName string
        LogicalDeleteDays double
        SaveDays double
        CheckpointDays double
        DirectoryVersionCacheDays double
        DiffCacheDays double
        Description string
        RecordSaves bool
    }
    Branch ||--|{ Reference : "has 1:N"
    Branch {
        BranchId Guid
        BranchName string
        OwnerId Guid
        OrganizationId Guid
        RepositoryId Guid
        UserId Guid
        PromotionEnabled bool
        CommitEnabled bool
        CheckpointEnabled bool
        SaveEnabled bool
        TagEnabled bool
        ExternalEnabled bool
        AutoRebaseEnabled bool
    }
    Repository ||--|{ DirectoryVersion : "has 1:N"
    Reference ||--|| DirectoryVersion : "refers to exactly 1"
    Reference {
        ReferenceId Guid
        DirectoryVersionId Guid
        Sha256Hash string
        ReferenceType ReferenceType
        ReferenceTest string
        Links ReferenceLinkType[]
    }
    DirectoryVersion {
        DirectoryVersionId Guid
        RepositoryId Guid
        RelativePath string
        Sha256Hash string
        Directories DirectoryVersionId[]
        Files FileVersion[]
    }
    DirectoryVersion ||--|{ FileVersion : "has 1:N"
    FileVersion {
        RepositoryId Guid
        RelativePath string
        Sha256Hash string
        IsBinary bool
        Size int64
        BlobUri string
    }
```


================================================
FILE: docs/Design and Motivations.md
================================================
# Design and Motivations

Hi, I'm Scott. I created Grace.

I'll use first-person singular in this document because I want to share what the early design and technology decisions were for Grace, and why I started writing it in the first place. I'll happily rewrite it in first-person plural when it's appropriate.

For shorter answers to some of these, please see [Frequently Asked Questions](Frequently%20asked%20questions.md).

---

## Table of Contents

[A word about Git](#a-word-about-git)

[User experience is everything](#user-experience-is-everything)

[The origin of Grace](#the-origin-of-grace)

[Perceived performance](#perceived-performance)

[CLI + Native GUI + Web UI + Web API](#cli--native-gui--web-ui--web-api)

[F# and Functional programming](#f-and-functional-programming)

[Source control isn't systems-level](#source-control-isnt-systems-level)

[Cloud-native version control](#cloud-native-version-control)

[Why Grace is centralized](#why-grace-is-centralized)

[Performance; or, Isn't centralized version control slower?](#performance-or-isnt-centralized-version-control-slower)

[How much Git should we keep?](#how-much-git-should-we-keep)

[Scalability](#scalability)

[Monorepos](#monorepos)

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## A word about Git

It's not possible to design a version control system (VCS) today without designing something that relates to, interfaces with, and/or somehow _just reacts to_ Git. In order to explain some of the choices I've made in Grace, I _have_ to mention Git. Mostly, of course, I'll do that if I think Grace is better in some way or other.

With that said, and just to be clear... I respect Git enormously. It will take years for any new VCS to approximate the feature-set of Git. Until a new one starts to gain momentum and gets a sustained programming effort behind it – open-source and community-supported – every new VCS will sort-of be a sketch compared to everything that Git can do.

The maintainers of Git are among the best programmers in the world. The way they continue to improve Git's scalability and performance, year-after-year, while maintaining compatibility with existing repositories, is an example of how to do world-impacting programming with skill and, dare I say, grace.

Git has been around for 17 years now, and it's not disappearing anytime soon. If you love Git, if it fits your needs well, I'm guessing you will be able to continue to use it for the next 15-20 years without a problem. (What source control might look like in 2042 is anyone's guess.)

### Git is dominant now, but...

Whether Git will remain the dominant version control system for that entire time is quite another question. I believe that _something else_ will capture people's imagination enough to get them to switch away from Git at some point. My guess about when that will happen is: soon-ish. Like, _something else_ is being created now-ish, \<waves hands\>±2 years. There are some wonderful source control projects going on right now that are exploring this space. I offer Grace in the hope that _it_ will be good enough to make people switch. Time will tell.

Git is amazing at what it does. I'm openly borrowing from Git where I think it's important to (ephemeral working directory, lightweight branching, SHA-256 hashes, and so much else).

I just think that it's a different time now. The constraints that existed in 2005 in terms of hardware and networking, the constraints that Git was designed to fit in, don't hold anymore. We can take advantage of current client and server and cloud capabilities to design something really different, and even better.

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## User experience is everything

Now that I've said nice things about Git....

### Git's UX is terrible.

 [I](https://xkcd.com/1597/) [hope](https://gracevcsdevelopment.blob.core.windows.net/static/RandomGitCommands.jpeg) [this](https://git-man-page-generator.lokaltog.net) [is](https://rakhim.org/honestly-undefined/13/) [not](https://gracevcsdevelopment.blob.core.windows.net/static/MemorizingSixGitCommands.jpg) [a](https://www.quora.com/Why-is-Git-so-hard-to-learn) [controversial](https://www.quora.com/If-I-think-Git-is-too-hard-to-learn-does-it-mean-that-I-dont-have-the-potential-to-be-a-developer) [statement](https://twitter.com/markrussinovich/status/1395143648191279105). And [I](https://twitter.com/robertskmiles/status/1431560311086137353) **[know](https://twitter.com/markrussinovich/status/1578451245249052672)** [I'm](https://ohshitgit.com/) [not](https://twitter.com/dvd848/status/1508528441519484931) [alone](https://twitter.com/shanselman/status/1102296651081760768) [in](https://www.linuxjournal.com/content/terrible-ideas-git) [thinking](https://blog.acolyer.org/2016/10/24/whats-wrong-with-git-a-conceptual-design-analysis/) [it](https://matt-rickard.com/the-terrible-ux-of-git).

Learning Git is far too hard. It's basically a hazing ritual that we put ourselves through as an industry. Git forces the user to understand far too much about its internals just to become a proficient user. Maybe 15%-20% of users really understand it.

Many of its regular users are literally afraid of it. Including me.

> Bad software, designed without empathy, that restricts people to only follow strict procedures to achieve one specific goal, has trained millions of people that software is cumbersome, inflexible, and even hostile and that users have to adapt to the machine, if they want to get anything done. The future of computing should be very much the opposite. Good software can augment the human experience by becoming the tool that’s needed in the moment, unrestricted by limitations in the physical world. It can become the personal dynamic medium through which exploring and expressing our ideas should become simpler rather than more difficult. [^StefanLesser]
> 
> \- Stefan Lesser

### Grace's UX is focused on simplicity

Grace is explicitly designed to be easy to use, and easy to understand.

It's as simple as possible. It has abstractions. Users don't have to know the details of how it works to use it.

Because of that, Grace has fewer concepts for users to understand to become and feel proficient in using it.

Grace formats output to make it as easy to read as possible, and also offers JSON output, minimal output, and silent output for each command. [^output]

And in a world where hybrid and remote work is growing, Grace offers entirely new experiences with a live, two-way channel between client and server, linking repository users together in new ways, including auto-rebasing immediately after promotions (which are merges, sort-of).

There's so much more to do in UX for version control. Grace is a platform for exploring where it can go next.

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## The origin of Grace

There was an informal Source Control Summit in November, 2020 that I had the opportunity to attend, and I had the chance to have some additional side conversations with a few of the other attendees.

The vibe I got from those interactions – and I want to emphasize that this was _my_ takeaway, and that I do not speak for anyone else – was that 1) we're all still just mining for incremental improvements in Git; 2) we're getting tired of Git and whatever else we're using; and 3) we're not sure what could come next that would change that.

That led me to sitting outside on my front porch in December, 2020 – still in pandemic lockdown, in the darkest month of a dark year – and starting to think about what **I** would want in a version control system.

It all started that first night with a few themes:

- It had to be easy-to-use. The pain of learning Git, and the continuing fear of it, has always been a sore spot for me, and, I know, for millions of others.
- It had to be cloud-native, so it could take advantage of the fast, cloud-scale computing that we're all used to in almost every other kind of software, and get away from using file servers.
- It had to have live synchronization between client and server – I was thinking of the OneDrive sync client as a good example – as the basis for being able to build important new features.
- It had to fundamentally break away from Git. No "Git client but a different backend". No "New client, but Git for the storage layer." No "it should speak Git protocol".

I just wanted to start with a blank sheet of paper, keep the things about Git that we all like, take advantage of modern cloud-native services, and get rid of the complexity.

Grace is the version control system that I'd want to use.

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## Perceived performance

Measuring actual performance of any significant system is important, and Grace will have performance benchmarks that can be run alongside other kinds of tests to ensure that regressions don't sneak in. I care deeply about performance.

What I care even more about is _perceived performance_. What I mean by that is something more subjective than just "how many milliseconds?" I mean: **does it _feel_ fast**?

Perceived performance includes not just how long something takes, but how long it takes relative to other things that a user is doing, and how consistent that time is from one command invocation to the next, to the next, to the next.

My experience is that running _fast enough_, _consistently_, is what gives the user the feeling that a system is fast and responsive.

That's what Grace is aiming for: both _fast_, and _consistent_. Fast + consistent means that users can develop expectations and muscle memory when using a command, and that running a command won't take them out of their flow.

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## CLI + Native GUI + Web UI + Web API

Another avenue for providing great UX is in providing choices about how to interact with Grace.

### CLI

Obviously, Grace starts with a command-line interface (CLI). I've designed it for ease-of-use. As much as possible, default values are used to save typing. By default, most output is shown in tables with borders, and is formatted to help guide your eyes to the important content. Grace can also provide output in minimal text, JSON, verbose, or no output at all (silent).

### Native GUI

Wait, what? No one does those anymore.

Yeah, well... the thing is, I really don't like Electron apps. Like, at all.

It's simply not possible to recreate the snappiness, the stick-to-your-finger-ness, the _certainty_ that the UI is reacting to you, that you get in a native app when you're writing in a browser. It's just not. I've been watching this for years now, and almost no one even tries.

"What about Visual Studio Code?" I hear someone say. It's among the best examples, for sure. But I don't _love_ it. Look how much programming and how many person-years have gone into it to make it OK. Look at the way they had to completely rewrite the terminal window output using Canvas because nothing else in a browser was fast enough.

I don't see a lot of other Electron apps getting nearly that level of effort and programming. And they're all just... not great.

I fear that, as an industry, we're failing our fellow human beings, and we're failing each other, by accepting second-rate UX on the first-rate hardware we have.

We're making this choice for one reason: our own convenience as programmers. We're prioritizing developer ergonomics over user experience, and claiming that it's for business reasons. And we're usually not making great UX with it.

We have tools today that can create native apps on multiple platforms from a single codebase, and that's what I'm taking advantage of. There's nothing in Grace's UX that I'm currently imagining that requires any complex user controls that can't be rendered in any of those tools... you know: text blocks, lines, borders, normal input fields and buttons / checkboxes / radio buttons. Maybe a tree view or some graphs if I'm feeling fancy.

We can provide incredible experiences when we take advantage of the hardware directly, and I intend to.

### Web UI

So, after all that... I'm creating a Web UI? What gives?

Browsers are great for browsing and light functionality, and that's all Grace will need.

### Web API

Grace Server itself is simply a modern, 2024-style Web API. If there's something you'd rather automate by calling the server directly, party on. Grace ships with a .NET SDK (because that's what the CLI + Native GUI + Web UI use), and that SDK is simply a projection of the Web API into a specific platform. It should be trivial to create similar SDK's for other languages and platforms.

It's about choices for the user. It's about understanding that sometimes the best way to share something is with a URL. And it's about providing a place that we can collaborate on what the default Grace's UI should look like.

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## F# and functional programming

### Grace is written primarily in F\#

The main reason for this is simple: **F# is my favorite programming language right now**. It's beautiful, and it feels lightweight, but it's really strongly-typed and very fast.

But... there are other reasons.

### Reconsidering object-oriented programming

Like many of my peers, I've been specializing in object-oriented (OO) code for a long time. For me, it was C++ starting in 1998, and then .NET starting with .NET Framework Beta 2 in June, 2001. I've written tens-of-thousands of lines of object-oriented code. In 2015, I started to learn Category Theory, and in 2017-18, I had the opportunity to work on a project at Microsoft Research that was written in F#. I went back to C# for a bit after that, but, the seed was planted, and in a small 2020 pandemic side project, I decided to use F# to _really_ learn to think functionally and put a little bit of that Category Theory to use.

After 20+ years of writing OO code, I've come to the conclusion, as have others, that we've hit a ceiling in quality and maintainability in object-oriented-ish code for anything beyond a medium-sized codebase. We've adopted many practices to cover up for the problems with OO, like dependency injection and automated unit testing so we can refactor safely, but the truth is that without a significant investment in Developer Experience, and sustained effort to just keep the code clean, many large OO projects become supremely brittle and hard to maintain.

You may disagree, and that's fine. There's a fair argument that when you design OO systems as message-passing systems (and Grace does this using the Actor pattern) they factor really well. I'm not saying it's not possible to have a large and still-flexible OO codebase, just that it's rare and takes deliberate effort to keep it that way.

Functional programming offers a new path to create large-scale codebases that sidestep these problems. No doubt, over the coming years, as more teams try functional code, we'll find anti-patterns that we need to deal with (and, no doubt, I have some of them in Grace), but having personally taken the mindset journey from OO to functional, my field report is: we'll benefit greatly as an industry if we take a more functional and declarative approach to coding. It can do wonders everywhere, not just in the UI frameworks where we've already seen the benefits.

Whether you choose Haskell, Scala, F#, Crystal, or some other functional language, I invite you to try functional programming. It's a journey, for sure, but it's so worth it. Not only will you learn a new way to think about organizing code, you'll become a better OO programmer for it.

### .NET is really nice to use, and well-supported

For those who haven't worked with it yet... .NET is great now. Really. Let me explain why.

The old days of .NET being a Windows-only framework are long-since over. .NET is fully cross-platform, suporting Windows, Linux, MacOS, Android, and iOS. It's the most well-loved framework according to [Stack Overflow's 2022 Developer Survey](https://survey.stackoverflow.co/2022/#section-most-popular-technologies-other-frameworks-and-libraries), as it was in [2021](https://insights.stackoverflow.com/survey/2021#section-most-popular-technologies-other-frameworks-and-libraries), and Microsoft has continued to pour work into making it faster, better, easier-to-use, and well-documented. NuGet, .NET's package manager, has community-supported packages for almost every technology one might wish to interface with.

In terms of performance, .NET has been near the top of the [Techempower Benchmarks](https://www.techempower.com/benchmarks/#section=data-r21&test=composite) for years, and the .NET team and community continue to find performance improvements in every release.

As far as developer experience, .NET is just a really nice place to spend time. The documentation is amazing, the examples and StackOverflow support are first-rate.

The .NET team has written a wonderful blog post called [What is .NET, and why should you choose it?](https://devblogs.microsoft.com/dotnet/why-dotnet/), the first of a series diving into the design of the platform.

Is it perfect? No, of course not. Nothing in our business is.

Is it "really good" or "great" a lot of the time? Does it continue to improve release after release? In my experience... yes, absolutely.

Will it be supported for a long time? .NET has great adoption in both open-source and enterprise shops. Unity, one of the most popular game engines, is written in C#. Microsoft itself runs many of its insanely large first-party Azure services on .NET, and that alone will keep .NET around and on a continuous improvement cycle for the forseeable future.

So, it's very fast, it has great corporate and community support, it runs on every major platform, and it's loved by those who use it. I'm not saying that other tech stacks aren't great, just that .NET is great now and well worth a long-term bet.

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## Source control isn't systems-level

I like things that go fast. My second programming language – at age 11 – was 6502 Assembler. I've written and read code in IBM 370 Assembler and 80x86 Assembler. I've written C and C++, and continue to pay attention to the wonderful work being led by [Herb Sutter](https://www.youtube.com/user/CppCon/search?query=herb%20sutter) and [Bjarne Stroustrup](https://www.youtube.com/user/CppCon/search?query=bjarne) to make C++ faster, safer, less verbose, and easier to use. I applaud the work by Mozilla and the Rust community to explore the space of safer, very fast systems programming. I consider any public talk by [Chandler Carruth](https://www.youtube.com/results?search_query=chandler+carruth) to be mandatory viewing.

I'm aware of what it means to be coding down-to-the-metal. I grew up on it, and still try to think in terms of hardware capabilities, even as I use higher-level frameworks like .NET.

With that said, the idea that version control systems have to be written in a systems-level language, just because they all used to be, isn't true, especially for a centralized VCS that's just a modern Web API and its clients.

Grace relies on external databases and object storage services, and so there's not much Git-style byte-level file manipulation. Given how fast .NET is (within 1% of native C++ when well-written), and the fact that network round-trips are involved in most things that Grace does, it's not likely that writing Grace in C++ or Rust would make a difference in perceived performance for users. A lot of the clock time during commands is spent on those network round-trips, even on the server. Using F# and .NET for the computation – i.e. for Grace itself – is more than fast enough compared to all of that.

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## Cloud-native version control

I've personally installed and maintained hundreds of servers and virtual machines in my career. I racked dozens of them myself. It seemed fun at the time. I'm over it.

That's why I'm a huge fan of Platform-as-a-Service (PaaS), and why Grace was imagined on its first day as a cloud-native system. I starting tracking the [Dapr project](https://dapr.io) as soon as it was announced, and saw it as a perfect solution for being able to write a cloud-native, PaaS-based system, while allowing everyone to choose their own deployment adventure.

### Your choice of services at deployment time

Grace runs on Dapr to allow you to choose which PaaS or cloud or even on-premises services it runs on.

The database, the observability platform, the service bus / event hub pieces, and more, will be configurable by you. Grace will run on anything Dapr supports.

### Object storage is different

Grace uses an object storage service to save each version of the files in a repo (i.e. Azure Blob Storage, AWS S3, Google Cloud Storage, etc.). Although Dapr does support pluggable object storage providers, using Dapr for Grace's object s
Download .txt
gitextract_t_kevelm/

├── .beads/
│   ├── .gitignore
│   ├── README.md
│   ├── config.yaml
│   ├── interactions.jsonl
│   ├── issues.jsonl
│   └── metadata.json
├── .config/
│   └── dotnet-tools.json
├── .gitattributes
├── .github/
│   ├── code_review_instructions.md
│   ├── copilot-instructions.md
│   └── workflows/
│       ├── dotnet.yml
│       └── validate.yml
├── .gitignore
├── .markdownlint.jsonc
├── AGENTS.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── REPO_INDEX.md
├── SECURITY.md
├── _endpoint_stub.txt
├── code_of_conduct.md
├── docs/
│   ├── .markdownlint.jsonc
│   ├── AI Submissions.md
│   ├── Authentication.md
│   ├── Branching strategy.md
│   ├── Continuous review.md
│   ├── Data types in Grace.md
│   ├── Design and Motivations.md
│   ├── Design concepts/
│   │   ├── Backups.md
│   │   └── Directory and file-level ACL's.md
│   ├── Frequently asked questions.md
│   ├── How Grace computes the SHA-256 value.md
│   ├── Mermaid diagrams.md
│   ├── The potential for misusing Grace.md
│   ├── What grace watch does.md
│   ├── Why Auto-Rebase isn't a problem.md
│   ├── Why Grace isn't just a version control system.md
│   └── Work items.md
├── global.json
├── prompts/
│   ├── ContentPack.prompt.md
│   ├── Grace issue summary.md
│   ├── Grace pull request summary.md
│   └── PlanPack.prompt.md
├── scripts/
│   ├── bootstrap.ps1
│   ├── collect-runtime-metadata.ps1
│   ├── dev-local.ps1
│   ├── install-githooks.ps1
│   ├── start-debuglocal.ps1
│   └── validate.ps1
├── src/
│   ├── .aspire/
│   │   └── settings.json
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .gitattributes
│   ├── .github/
│   │   ├── copilot-instructions.md
│   │   └── workflows/
│   │       └── deploy-to-app-service.yml
│   ├── AGENTS.md
│   ├── CountLines.ps1
│   ├── Create-Grace-Objects.ps1
│   ├── Directory.Build.props
│   ├── Grace.Actors/
│   │   ├── AGENTS.md
│   │   ├── AccessControl.Actor.fs
│   │   ├── ActorProxy.Extensions.Actor.fs
│   │   ├── Artifact.Actor.fs
│   │   ├── Branch.Actor.fs
│   │   ├── BranchName.Actor.fs
│   │   ├── CodeGenAttribute.Actor.fs
│   │   ├── Constants.Actor.fs
│   │   ├── Context.Actor.fs
│   │   ├── Diff.Actor.fs
│   │   ├── DirectoryAppearance.Actor.fs
│   │   ├── DirectoryVersion.Actor.fs
│   │   ├── Extensions/
│   │   │   └── MemoryCache.Extensions.Actor.fs
│   │   ├── FileAppearance.Actor.fs
│   │   ├── GlobalLock.Actor.fs
│   │   ├── Grace.Actors.fsproj
│   │   ├── GrainRepository.Actor.fs
│   │   ├── Interfaces.Actor.fs
│   │   ├── NamedSection.Actor.fs
│   │   ├── Organization.Actor.fs
│   │   ├── OrganizationName.Actor.fs
│   │   ├── Owner.Actor.fs
│   │   ├── OwnerName.Actor.fs
│   │   ├── PersonalAccessToken.Actor.fs
│   │   ├── Policy.Actor.fs
│   │   ├── PromotionQueue.Actor.fs
│   │   ├── PromotionSet.Actor.fs
│   │   ├── Reference.Actor.fs
│   │   ├── Reminder.Actor.fs
│   │   ├── Repository.Actor.fs
│   │   ├── Repository.Actor.fs (ApplyEvent Method)
│   │   ├── RepositoryName.Actor.fs
│   │   ├── RepositoryPermission.Actor.fs
│   │   ├── Review.Actor.fs
│   │   ├── Services.Actor.fs
│   │   ├── Timing.Actor.fs
│   │   ├── Types.Actor.fs
│   │   ├── User.Actor.fs
│   │   ├── ValidationResult.Actor.fs
│   │   ├── ValidationSet.Actor.fs
│   │   ├── WorkItem.Actor.fs
│   │   ├── WorkItemNumber.Actor.fs
│   │   └── WorkItemNumberCounter.Actor.fs
│   ├── Grace.Aspire.AppHost/
│   │   ├── AGENTS.md
│   │   ├── Grace.Aspire.AppHost.csproj
│   │   ├── Program.Aspire.AppHost.cs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   └── appsettings.json
│   ├── Grace.Aspire.ServiceDefaults/
│   │   ├── Extensions.cs
│   │   └── Grace.Aspire.ServiceDefaults.csproj
│   ├── Grace.Authorization.Tests/
│   │   ├── AuthorizationSemantics.Tests.fs
│   │   ├── ClaimMapping.Tests.fs
│   │   ├── EndpointAuthorizationManifest.Tests.fs
│   │   ├── Grace.Authorization.Tests.fsproj
│   │   ├── PathPermissions.Tests.fs
│   │   ├── PermissionEvaluator.Tests.fs
│   │   ├── PersonalAccessToken.Tests.fs
│   │   └── Program.fs
│   ├── Grace.CLI/
│   │   ├── AGENTS.md
│   │   ├── Command/
│   │   │   ├── Access.CLI.fs
│   │   │   ├── Admin.CLI.fs
│   │   │   ├── Agent.CLI.fs
│   │   │   ├── Auth.CLI.fs
│   │   │   ├── Branch.CLI.fs
│   │   │   ├── Candidate.CLI.fs
│   │   │   ├── Common.CLI.fs
│   │   │   ├── Config.CLI.fs
│   │   │   ├── Connect.CLI.fs
│   │   │   ├── Diff.CLI.fs
│   │   │   ├── DirectoryVersion.CLI.fs
│   │   │   ├── History.CLI.fs
│   │   │   ├── Maintenance.CLI.fs
│   │   │   ├── Organization.CLI.fs
│   │   │   ├── Owner.CLI.fs
│   │   │   ├── PromotionSet.CLI.fs
│   │   │   ├── Queue.CLI.fs
│   │   │   ├── Reference.CLI.fs
│   │   │   ├── Repository.CLI.fs
│   │   │   ├── Review.CLI.fs
│   │   │   ├── Services.CLI.fs
│   │   │   ├── Watch.CLI.fs
│   │   │   └── WorkItem.CLI.fs
│   │   ├── Conversion.md
│   │   ├── Grace.CLI.fsproj
│   │   ├── HistoryStorage.CLI.fs
│   │   ├── LocalStateDb.CLI.fs
│   │   ├── Log.CLI.fs
│   │   ├── Program.CLI.fs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   ├── Text.CLI.fs
│   │   └── packages-microsoft-prod.deb
│   ├── Grace.CLI.LocalStateDb.Worker/
│   │   ├── Grace.CLI.LocalStateDb.Worker.fsproj
│   │   └── Program.fs
│   ├── Grace.CLI.Tests/
│   │   ├── AGENTS.md
│   │   ├── Agent.CLI.Tests.fs
│   │   ├── Auth.Tests.fs
│   │   ├── AuthTokenBundle.Tests.fs
│   │   ├── Connect.CLI.Tests.fs
│   │   ├── Grace.CLI.Tests.fsproj
│   │   ├── History.CLI.Tests.fs
│   │   ├── HistoryStorage.CLI.Tests.fs
│   │   ├── LocalStateDb.Tests.fs
│   │   ├── Program.CLI.Tests.fs
│   │   ├── Program.fs
│   │   ├── PromotionSet.CLI.Tests.fs
│   │   ├── Queue.CLI.Tests.fs
│   │   ├── Review.CLI.Tests.fs
│   │   ├── Watch.Tests.fs
│   │   └── WorkItem.CLI.Tests.fs
│   ├── Grace.Load/
│   │   ├── Grace.Load.fsproj
│   │   ├── Program.Load.fs
│   │   └── Properties/
│   │       └── launchSettings.json
│   ├── Grace.Orleans.CodeGen/
│   │   ├── Declaration.Orleans.CodeGen.cs
│   │   ├── Grace.Orleans.CodeGen.csproj
│   │   └── instructions.md
│   ├── Grace.SDK/
│   │   ├── AGENTS.md
│   │   ├── Access.SDK.fs
│   │   ├── Admin.SDK.fs
│   │   ├── Artifact.SDK.fs
│   │   ├── Auth.SDK.fs
│   │   ├── Branch.SDK.fs
│   │   ├── Common.SDK.fs
│   │   ├── Diff.SDK.fs
│   │   ├── DirectoryVersion.SDK.fs
│   │   ├── Grace.SDK.fsproj
│   │   ├── Organization.SDK.fs
│   │   ├── Owner.SDK.fs
│   │   ├── PersonalAccessToken.SDK.fs
│   │   ├── Policy.SDK.fs
│   │   ├── PromotionSet.SDK.fs
│   │   ├── Queue.SDK.fs
│   │   ├── Repository.SDK.fs
│   │   ├── Review.SDK.fs
│   │   ├── Storage.SDK.fs
│   │   ├── ValidationResult.SDK.fs
│   │   ├── ValidationSet.SDK.fs
│   │   └── WorkItem.SDK.fs
│   ├── Grace.Server/
│   │   ├── AGENTS.md
│   │   ├── Access.Server.fs
│   │   ├── ApplicationContext.Server.fs
│   │   ├── Artifact.Server.fs
│   │   ├── Auth.Server.fs
│   │   ├── Branch.Server.fs
│   │   ├── CorrelationId.Server.fs
│   │   ├── DerivedComputation.Server.fs
│   │   ├── Diff.Server.fs
│   │   ├── DirectoryVersion.Server.fs
│   │   ├── Dockerfile
│   │   ├── Eventing.Server.fs
│   │   ├── Grace.Server.fsproj
│   │   ├── Middleware/
│   │   │   ├── CorrelationId.Middleware.fs
│   │   │   ├── Fake.Middleware.fs
│   │   │   ├── HttpSecurityHeaders.Middleware.fs
│   │   │   ├── LogAuthorizationFailure.Middleware.fs
│   │   │   ├── LogRequestHeaders.Middleware.fs
│   │   │   ├── Timing.Middleware.fs
│   │   │   └── ValidateIds.Middleware.fs
│   │   ├── Notification.Server.fs
│   │   ├── Organization.Server.fs
│   │   ├── OrleansFilters.Server.fs
│   │   ├── Owner.Server.fs
│   │   ├── PartitionKeyProvider.fs
│   │   ├── Policy.Server.fs
│   │   ├── Program.Server.fs
│   │   ├── PromotionSet.Server.fs
│   │   ├── Properties/
│   │   │   ├── PublishProfiles/
│   │   │   │   └── DisableContainerBuild.pubxml
│   │   │   └── launchSettings.json
│   │   ├── Queue.Server.fs
│   │   ├── Reminder.Server.fs
│   │   ├── ReminderService.Server.fs
│   │   ├── Repository.Server.fs
│   │   ├── Review.Server.fs
│   │   ├── ReviewAnalysis.Server.fs
│   │   ├── ReviewModels.Server.fs
│   │   ├── Security/
│   │   │   ├── AuthorizationMiddleware.Server.fs
│   │   │   ├── ClaimMapping.Server.fs
│   │   │   ├── ClaimsTransformation.Server.fs
│   │   │   ├── EndpointAuthorizationManifest.Server.fs
│   │   │   ├── ExternalAuthConfig.Server.fs
│   │   │   ├── PermissionEvaluator.Server.fs
│   │   │   ├── PersonalAccessTokenAuth.Server.fs
│   │   │   ├── PrincipalMapper.Server.fs
│   │   │   └── TestAuth.Server.fs
│   │   ├── Services.Server.fs
│   │   ├── Startup.Server.fs
│   │   ├── Storage.Server.fs
│   │   ├── ValidationResult.Server.fs
│   │   ├── ValidationSet.Server.fs
│   │   ├── Validations.Server.fs
│   │   ├── WorkItem.Server.fs
│   │   ├── appsettings.json
│   │   └── web.config
│   ├── Grace.Server.Tests/
│   │   ├── AGENTS.md
│   │   ├── Access.Server.Tests.fs
│   │   ├── AspireTestHost.fs
│   │   ├── Auth.Server.Tests.fs
│   │   ├── AuthMapping.Unit.Tests.fs
│   │   ├── Authorization.Unit.Tests.fs
│   │   ├── Eventing.Server.Tests.fs
│   │   ├── Evidence.Determinism.Tests.fs
│   │   ├── General.Server.Tests.fs
│   │   ├── Grace.Server.Tests.fsproj
│   │   ├── Notification.Server.Tests.fs
│   │   ├── OrleansFilters.Server.Tests.fs
│   │   ├── Owner.Server.Tests.fs
│   │   ├── Policy.Determinism.Tests.fs
│   │   ├── Policy.Validation.Derived.Tests.fs
│   │   ├── Program.fs
│   │   ├── PromotionSet.CommandValidation.Tests.fs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   ├── Queue.Server.Tests.fs
│   │   ├── Repository.Server.Tests.fs
│   │   ├── Review.Server.Tests.fs
│   │   ├── ReviewNotes.Determinism.Tests.fs
│   │   ├── Services.EffectivePromotion.Tests.fs
│   │   ├── Slop.Server.Tests.fs
│   │   ├── Smoke.Server.Tests.fs
│   │   ├── Validation.Artifact.Contract.Tests.fs
│   │   ├── Validations.Server.Tests.fs
│   │   ├── WorkItem.Integration.Server.Tests.fs
│   │   ├── WorkItem.Server.Tests.fs
│   │   └── appsettings.json
│   ├── Grace.Shared/
│   │   ├── AGENTS.md
│   │   ├── Authorization.Shared.fs
│   │   ├── AzureEnvironment.Shared.fs
│   │   ├── BaselineDrift.Shared.fs
│   │   ├── Client/
│   │   │   ├── Configuration.Shared.fs
│   │   │   ├── Theme.Shared.fs
│   │   │   └── UserConfiguration.Shared.fs
│   │   ├── Combinators.fs
│   │   ├── Constants.Shared.fs
│   │   ├── Converters/
│   │   │   └── BranchDtoConverter.Shared.fs
│   │   ├── Diff.Shared.fs
│   │   ├── Dto/
│   │   │   └── Dto.Shared.fs
│   │   ├── Evidence.Shared.fs
│   │   ├── Extensions.Shared.fs
│   │   ├── Grace.Shared.fsproj
│   │   ├── Monikers.imagemanifest
│   │   ├── Parameters/
│   │   │   ├── Access.Parameters.fs
│   │   │   ├── Artifact.Parameters.fs
│   │   │   ├── Auth.Parameters.fs
│   │   │   ├── Branch.Parameters.fs
│   │   │   ├── Common.Parameters.fs
│   │   │   ├── Diff.Parameters.fs
│   │   │   ├── Directory.Parameters.fs
│   │   │   ├── Organization.Parameters.fs
│   │   │   ├── Owner.Parameters.fs
│   │   │   ├── Policy.Parameters.fs
│   │   │   ├── PromotionSet.Parameters.fs
│   │   │   ├── Queue.Parameters.fs
│   │   │   ├── Reference.Parameters.fs
│   │   │   ├── Reminder.Parameters.fs
│   │   │   ├── Repository.Parameters.fs
│   │   │   ├── Review.Parameters.fs
│   │   │   ├── Storage.Parameters.fs
│   │   │   ├── Validation.Parameters.fs
│   │   │   └── WorkItem.Parameters.fs
│   │   ├── Resources/
│   │   │   ├── Text/
│   │   │   │   ├── Languages.Resources.fs
│   │   │   │   └── en-US.fs
│   │   │   └── Utilities.Resources.fs
│   │   ├── ReviewNotes.Shared.fs
│   │   ├── Services.Shared.fs
│   │   ├── Utilities.Shared.fs
│   │   └── Validation/
│   │       ├── Common.Validation.fs
│   │       ├── Connect.Validation.fs
│   │       ├── Errors.Validation.fs
│   │       ├── Repository.Validation.fs
│   │       └── Utilities.Validation.fs
│   ├── Grace.Types/
│   │   ├── AGENTS.md
│   │   ├── Artifact.Types.fs
│   │   ├── Auth.Types.fs
│   │   ├── Authorization.Types.fs
│   │   ├── Automation.Types.fs
│   │   ├── Branch.Types.fs
│   │   ├── Diff.Types.fs
│   │   ├── DirectoryVersion.Types.fs
│   │   ├── Events.Types.fs
│   │   ├── Grace.Types.fsproj
│   │   ├── Organization.Types.fs
│   │   ├── Owner.Types.fs
│   │   ├── PersonalAccessToken.Types.fs
│   │   ├── Policy.Types.fs
│   │   ├── PromotionSet.Types.fs
│   │   ├── Queue.Types.fs
│   │   ├── Reference.Types.fs
│   │   ├── Reminder.Types.fs
│   │   ├── Repository.Types.fs
│   │   ├── RequiredAction.Types.fs
│   │   ├── Review.Types.fs
│   │   ├── Types.Types.fs
│   │   ├── Validation.Types.fs
│   │   └── WorkItem.Types.fs
│   ├── Grace.Types.Tests/
│   │   ├── AGENTS.md
│   │   ├── Automation.Types.Tests.fs
│   │   ├── Grace.Types.Tests.fsproj
│   │   ├── Program.fs
│   │   ├── PromotionSet.ConflictModel.Types.Tests.fs
│   │   ├── PromotionSet.Types.Tests.fs
│   │   ├── Queue.Types.Tests.fs
│   │   ├── Validation.Types.Tests.fs
│   │   └── WorkItem.Types.Tests.fs
│   ├── Grace.slnx
│   ├── OpenAPI/
│   │   ├── Branch.Components.OpenAPI.yaml
│   │   ├── Branch.Paths.OpenAPI.yaml
│   │   ├── Diff.Components.OpenAPI.yaml
│   │   ├── Diff.Paths.OpenAPI.yaml
│   │   ├── Directory.Components.OpenAPI.yaml
│   │   ├── Directory.Paths.OpenAPI.yaml
│   │   ├── Dto.Components.OpenAPI.yaml
│   │   ├── Grace.OpenAPI.yaml
│   │   ├── Main.OpenAPI.yaml
│   │   ├── Organization.Components.OpenAPI.yaml
│   │   ├── Organization.Paths.OpenAPI.yaml
│   │   ├── Owner.Components.OpenAPI.yaml
│   │   ├── Owner.Paths.OpenAPI.yaml
│   │   ├── Reference.Components.OpenAPI.yaml
│   │   ├── Repository.Components.OpenAPI.yaml
│   │   ├── Repository.Paths.OpenAPI.yaml
│   │   ├── Responses.OpenAPI.yaml
│   │   ├── Shared.Components.OpenAPI.yaml
│   │   └── Shared2.Components.OpenAPI.yaml
│   ├── Processing file system changes.md
│   ├── docs/
│   │   ├── ASPIRE_SETUP.md
│   │   └── ENVIRONMENT.md
│   ├── fantomas-config.json
│   ├── launchSettings.json
│   ├── nuget.config
│   └── packages-microsoft-prod.deb
└── update-contributing.skill
Download .txt
SYMBOL INDEX (17 symbols across 2 files)

FILE: src/Grace.Aspire.AppHost/Program.Aspire.AppHost.cs
  class Program (line 21) | public partial class Program
    method Main (line 27) | private static void Main(string[] args)
    method ResolveSetting (line 469) | private static string? ResolveSetting(IConfiguration configuration, st...
    method GetRequiredSetting (line 496) | private static string GetRequiredSetting(IConfiguration configuration,...
    method AddOptionalEnvironment (line 509) | private static void AddOptionalEnvironment(
    method LogForwardedSettings (line 523) | private static void LogForwardedSettings(string label, IList<string> f...
    method CleanupDockerContainers (line 535) | private static void CleanupDockerContainers(IEnumerable<string> contai...
    method GetAvailableTcpPort (line 574) | private static int GetAvailableTcpPort()
    method BuildCosmosEmulatorConnectionString (line 583) | private static ReferenceExpression BuildCosmosEmulatorConnectionString(
    method CreateServiceBusConfiguration (line 633) | private static void CreateServiceBusConfiguration(string configFilePat...

FILE: src/Grace.Aspire.ServiceDefaults/Extensions.cs
  class Extensions (line 12) | public static class Extensions
    method AddServiceDefaults (line 14) | public static IHostApplicationBuilder AddServiceDefaults(this IHostApp...
    method ConfigureOpenTelemetry (line 34) | public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHos...
    method AddOpenTelemetryExporters (line 66) | private static IHostApplicationBuilder AddOpenTelemetryExporters(this ...
    method AddDefaultHealthChecks (line 88) | public static IHostApplicationBuilder AddDefaultHealthChecks(this IHos...
    method MapDefaultEndpoints (line 97) | public static WebApplication MapDefaultEndpoints(this WebApplication app)
    method AddBuiltInMeters (line 114) | private static MeterProviderBuilder AddBuiltInMeters(this MeterProvide...
Condensed preview — 389 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,977K chars).
[
  {
    "path": ".beads/.gitignore",
    "chars": 945,
    "preview": "# SQLite databases\n*.db\n*.db?*\n*.db-journal\n*.db-wal\n*.db-shm\n\n# Daemon runtime files\ndaemon.lock\ndaemon.log\ndaemon.pid\n"
  },
  {
    "path": ".beads/README.md",
    "chars": 2240,
    "preview": "# Beads - AI-Native Issue Tracking\n\nWelcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-n"
  },
  {
    "path": ".beads/config.yaml",
    "chars": 2262,
    "preview": "# Beads Configuration File\n# This file configures default behavior for all bd commands in this repository\n# All settings"
  },
  {
    "path": ".beads/interactions.jsonl",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".beads/issues.jsonl",
    "chars": 64137,
    "preview": "{\"id\":\"Grace-14w\",\"title\":\"P1 Add src/docs/ENVIRONMENT.md from EnvironmentVariables\",\"description\":\"Document env vars an"
  },
  {
    "path": ".beads/metadata.json",
    "chars": 93,
    "preview": "{\n  \"database\": \"beads.db\",\n  \"jsonl_export\": \"issues.jsonl\",\n  \"last_bd_version\": \"0.40.0\"\n}"
  },
  {
    "path": ".config/dotnet-tools.json",
    "chars": 158,
    "preview": "{\n  \"version\": 1,\n  \"isRoot\": true,\n  \"tools\": {\n    \"fantomas-tool\": {\n      \"version\": \"4.7.9\",\n      \"commands\": [\n  "
  },
  {
    "path": ".gitattributes",
    "chars": 70,
    "preview": "\n# Use bd merge for beads JSONL files\n.beads/issues.jsonl merge=beads\n"
  },
  {
    "path": ".github/code_review_instructions.md",
    "chars": 2357,
    "preview": "# Grace Code Review instructions\n\nThis file is intended to be the guidance for GitHub Copilot to perform automated code "
  },
  {
    "path": ".github/copilot-instructions.md",
    "chars": 2277,
    "preview": "# Copilot Operating Instructions for Grace\n\n## Load Repository Guidance\n\n-   Always begin by reviewing `src/agents.md`; "
  },
  {
    "path": ".github/workflows/dotnet.yml",
    "chars": 727,
    "preview": "name: .NET Restore / Build / Test\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  bu"
  },
  {
    "path": ".github/workflows/validate.yml",
    "chars": 1383,
    "preview": "name: Validate\n\non:\n  pull_request:\n    branches: [ main ]\n  push:\n    branches: [ main ]\n  schedule:\n    - cron: \"0 3 *"
  },
  {
    "path": ".gitignore",
    "chars": 3624,
    "preview": "## Ignore Visual Studio temporary files, build results, and\r\n## files generated by popular Visual Studio add-ons.\r\n\r\n# G"
  },
  {
    "path": ".markdownlint.jsonc",
    "chars": 98,
    "preview": "{\n  // Global markdownlint configuration for this repo.\n  \"MD013\": {\n    \"line_length\": 120\n  }\n}\n"
  },
  {
    "path": "AGENTS.md",
    "chars": 2294,
    "preview": "# Agent Instructions\n\nOther `AGENTS.md` files exist in subdirectories, refer to them for more specific context.\n\n## Agen"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 8898,
    "preview": "# Contributing to Grace Version Control System\r\n\r\nThanks for considering a contribution to **Grace Version Control Syste"
  },
  {
    "path": "LICENSE.md",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2022 Scott Arbeit\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 13960,
    "preview": "# Grace - Version Control for the AI Era\r\n\r\n<!-- markdownlint-disable MD013 -->\r\n\r\ngrace _(n)_ -\r\n\r\n1. elegance and beau"
  },
  {
    "path": "REPO_INDEX.md",
    "chars": 2724,
    "preview": "# REPO_INDEX.md (Grace jump table)\r\n\r\nThis file is a machine-oriented index to help tools and humans quickly find the ri"
  },
  {
    "path": "SECURITY.md",
    "chars": 1683,
    "preview": "# Security Policy\r\n\r\nThis project is currently **Alpha** quality. Security hardening is ongoing and may not yet cover al"
  },
  {
    "path": "_endpoint_stub.txt",
    "chars": 8722,
    "preview": "    endpoint \"GET\" \"/\" Authenticated\r\n    endpoint \"POST\" \"/access/checkPermission\" Authenticated\r\n    endpoint \"POST\" \""
  },
  {
    "path": "code_of_conduct.md",
    "chars": 1162,
    "preview": "# Grace - Code of Conduct\r\n\r\n![](./Assets/Orange3.svg)\r\n\r\n## Culture\r\n\r\nCulture is not a physical thing. You can't _touc"
  },
  {
    "path": "docs/.markdownlint.jsonc",
    "chars": 119,
    "preview": "{\n  // For the /docs directory, disable line length rule. We're writing prose.\n  \"MD013\": {\n    \"enabled\": false\n  }\n}\n"
  },
  {
    "path": "docs/AI Submissions.md",
    "chars": 4171,
    "preview": "# AI Submissions for Grace\n\nGrace welcomes AI-assisted contributions when they are transparent, reproducible, and techni"
  },
  {
    "path": "docs/Authentication.md",
    "chars": 25665,
    "preview": "# Authentication\r\n\r\n<!-- markdownlint-configure-file {\"MD013\": {\"line_length\": 120}} -->\r\n\r\nThis document describes how "
  },
  {
    "path": "docs/Branching strategy.md",
    "chars": 4658,
    "preview": "![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)\n\n# Simplified branching strategy\n\n> Quick note"
  },
  {
    "path": "docs/Continuous review.md",
    "chars": 5309,
    "preview": "# Continuous review\n\nContinuous review tracks promotion-set candidates, evaluates gates, and records\nreview artifacts in"
  },
  {
    "path": "docs/Data types in Grace.md",
    "chars": 14320,
    "preview": "# Data types in Grace\n\nGrace uses a fairly simple data structure to keep track of everything. It's more robust than Git'"
  },
  {
    "path": "docs/Design and Motivations.md",
    "chars": 36535,
    "preview": "# Design and Motivations\n\nHi, I'm Scott. I created Grace.\n\nI'll use first-person singular in this document because I wan"
  },
  {
    "path": "docs/Design concepts/Backups.md",
    "chars": 3694,
    "preview": "# Backups in Grace\n\nLike any computer system, Grace needs to have backups, and to test restoration from those backups.\n\n"
  },
  {
    "path": "docs/Design concepts/Directory and file-level ACL's.md",
    "chars": 34,
    "preview": "# Directory and File-level ACL's\n\n"
  },
  {
    "path": "docs/Frequently asked questions.md",
    "chars": 11956,
    "preview": "# Frequently Asked Questions\n\n(_or, what I imagine they might be_)\n\nFor deeper answers to some of these, please read [De"
  },
  {
    "path": "docs/How Grace computes the SHA-256 value.md",
    "chars": 8379,
    "preview": "# Computing the SHA-256 value for files and directories\n\n## Introduction\n\nGrace uses the [SHA-256](https://en.wikipedia."
  },
  {
    "path": "docs/Mermaid diagrams.md",
    "chars": 8122,
    "preview": "# Mermaid diagrams\r\n\r\n## Starting state\r\n\r\n```mermaid\r\n%%{init: { 'logLevel': 'debug', 'theme': 'default', 'gitGraph': {"
  },
  {
    "path": "docs/The potential for misusing Grace.md",
    "chars": 3183,
    "preview": "# The potential for misusing Grace\n\nSomething I've thought about since the beginning of designing Grace is: how do we pr"
  },
  {
    "path": "docs/What grace watch does.md",
    "chars": 10835,
    "preview": "# What `grace watch` does\n\n## Introduction\n\nOne of the most important pieces of Grace is `grace watch`. `grace watch` is"
  },
  {
    "path": "docs/Why Auto-Rebase isn't a problem.md",
    "chars": 5556,
    "preview": "# Why Auto-Rebase isn't a problem\n\nOne of the more common feedback items I've gotten about Grace is around the idea of "
  },
  {
    "path": "docs/Why Grace isn't just a version control system.md",
    "chars": 3598,
    "preview": "# Why Grace isn't just a version control system\r\n\r\n## New circumstances require new tools\r\n\r\nWhen I first created Grace,"
  },
  {
    "path": "docs/Work items.md",
    "chars": 4935,
    "preview": "# Work items\n\nWork items are durable, event-sourced records for a unit of work in Grace.\nThey capture intent (title and "
  },
  {
    "path": "global.json",
    "chars": 80,
    "preview": "{\n  \"sdk\": {\n    \"version\": \"10.0.100\",\n    \"rollForward\": \"latestPatch\"\n  }\n}\r\n"
  },
  {
    "path": "prompts/ContentPack.prompt.md",
    "chars": 4565,
    "preview": "You are operating inside the Grace repo as an expert .NET + F# engineer and “no vibes allowed” research agent.\r\n\r\nGOAL\r\n"
  },
  {
    "path": "prompts/Grace issue summary.md",
    "chars": 8829,
    "preview": "# Grace Issue Summary Prompt\r\n\r\nUse this prompt to produce a complete GitHub issue body for the Grace repository.\r\n\r\n## "
  },
  {
    "path": "prompts/Grace pull request summary.md",
    "chars": 3784,
    "preview": "# Grace PR Summary Prompt\n\nUse this prompt to produce a complete GitHub pull request body for the Grace repository.\n\n## "
  },
  {
    "path": "prompts/PlanPack.prompt.md",
    "chars": 4477,
    "preview": "You are operating inside the Grace repo as an expert .NET + F# engineer and planning/spec agent.\r\n\r\nGOAL\r\nCreate (or upd"
  },
  {
    "path": "scripts/bootstrap.ps1",
    "chars": 2341,
    "preview": "[CmdletBinding()]\nparam(\n    [switch]$SkipDocker,\n    [switch]$CI\n)\n\nSet-StrictMode -Version Latest\n$ErrorActionPreferen"
  },
  {
    "path": "scripts/collect-runtime-metadata.ps1",
    "chars": 23757,
    "preview": "[CmdletBinding()]\nparam(\n    [string]$WorkspacePath = (Get-Location).Path,\n    [string]$StatusText,\n    [string]$StatusF"
  },
  {
    "path": "scripts/dev-local.ps1",
    "chars": 1782,
    "preview": "#!/usr/bin/env pwsh\n<#\n  Compatibility wrapper for local onboarding.\n\n  Canonical script:\n    pwsh ./scripts/start-debug"
  },
  {
    "path": "scripts/install-githooks.ps1",
    "chars": 2504,
    "preview": "[CmdletBinding()]\nparam(\n    [switch]$Uninstall,\n    [switch]$Force\n)\n\nSet-StrictMode -Version Latest\n$ErrorActionPrefer"
  },
  {
    "path": "scripts/start-debuglocal.ps1",
    "chars": 32233,
    "preview": "#!/usr/bin/env pwsh\n<#\n  Canonical local onboarding script for Grace.\n\n  It starts Aspire with the DebugLocal launch pro"
  },
  {
    "path": "scripts/validate.ps1",
    "chars": 7424,
    "preview": "[CmdletBinding()]\nparam(\n    [switch]$Fast,\n    [switch]$Full,\n    [switch]$SkipFormat,\n    [switch]$SkipBuild,\n    [swi"
  },
  {
    "path": "src/.aspire/settings.json",
    "chars": 74,
    "preview": "{\n  \"appHostPath\": \"../Grace.Aspire.AppHost/Grace.Aspire.AppHost.csproj\"\n}"
  },
  {
    "path": "src/.dockerignore",
    "chars": 316,
    "preview": "**/.classpath\n**/.dockerignore\n**/.env\n**/.git\n**/.gitignore\n**/.project\n**/.settings\n**/.toolstarget\n**/.vs\n**/.vscode\n"
  },
  {
    "path": "src/.editorconfig",
    "chars": 941,
    "preview": "# http://editorconfig.org\n\nroot = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\ninsert_final_newline = true\ntrim_trailing_w"
  },
  {
    "path": "src/.gitattributes",
    "chars": 612,
    "preview": "# Use bd merge for beads JSONL files\n.beads/issues.jsonl merge=beads\n\n# Ensure all text files use LF line endings\n* text"
  },
  {
    "path": "src/.github/copilot-instructions.md",
    "chars": 4955,
    "preview": "# GitHub Copilot Instructions for Beads\n\n## Project Overview\n\n**beads** (command: `bd`) is a Git-backed issue tracker de"
  },
  {
    "path": "src/.github/workflows/deploy-to-app-service.yml",
    "chars": 2684,
    "preview": "name: build-and-deploy-grace-server\n\non:\n  push:\n    branches:\n      - main\n      - develop\n  pull_request:\n    branches"
  },
  {
    "path": "src/AGENTS.md",
    "chars": 4075,
    "preview": "# Grace Repository Agents Guide\n\nAgents operating under `D:\\Source\\Grace\\src` should follow this playbook alongside the "
  },
  {
    "path": "src/CountLines.ps1",
    "chars": 779,
    "preview": "$codeFiles = Get-ChildItem -Include *.cs,*.csproj,*.fs,*.fsproj,*.yml,*.yaml,*.md -File -Recurse\n$totalLines = 0\n$files "
  },
  {
    "path": "src/Create-Grace-Objects.ps1",
    "chars": 6976,
    "preview": "$startTime = Get-Date\n$iterations = 50\n\n1..$iterations | ForEach-Object -Parallel {\n    Set-Alias -Name grace -Value D:\\"
  },
  {
    "path": "src/Directory.Build.props",
    "chars": 625,
    "preview": "<Project>\n    <PropertyGroup>\n        <AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>\n        <Pro"
  },
  {
    "path": "src/Grace.Actors/AGENTS.md",
    "chars": 1442,
    "preview": "# Grace.Actors Agents Guide\n\nStart with `../AGENTS.md` for global rules before working on Orleans code.\n\n## Purpose\n- De"
  },
  {
    "path": "src/Grace.Actors/AccessControl.Actor.fs",
    "chars": 9663,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Interfaces\nopen Grace.Sh"
  },
  {
    "path": "src/Grace.Actors/ActorProxy.Extensions.Actor.fs",
    "chars": 23201,
    "preview": "namespace Grace.Actors.Extensions\n\nopen Grace.Actors.Extensions.MemoryCache\nopen Grace.Actors.Constants\nopen Grace.Actor"
  },
  {
    "path": "src/Grace.Actors/Artifact.Actor.fs",
    "chars": 5663,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Interfaces\nopen Grace.Ac"
  },
  {
    "path": "src/Grace.Actors/Branch.Actor.fs",
    "chars": 45659,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/BranchName.Actor.fs",
    "chars": 1385,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.MemoryCache\no"
  },
  {
    "path": "src/Grace.Actors/CodeGenAttribute.Actor.fs",
    "chars": 168,
    "preview": "namespace Grace.Actors\n\nopen System.Runtime.CompilerServices\n\n[<assembly: InternalsVisibleTo(\"Grace.Orleans.CodeGen\")>]\n"
  },
  {
    "path": "src/Grace.Actors/Constants.Actor.fs",
    "chars": 4999,
    "preview": "namespace Grace.Actors\n\nopen Grace.Shared\nopen Grace.Types.Types\nopen Grace.Shared.Utilities\nopen NodaTime\nopen System\no"
  },
  {
    "path": "src/Grace.Actors/Context.Actor.fs",
    "chars": 3324,
    "preview": "namespace Grace.Actors\n\nopen Azure.Core\nopen Azure.Identity\nopen Azure.Storage.Blobs\nopen Grace.Actors.Types\nopen Grace."
  },
  {
    "path": "src/Grace.Actors/Diff.Actor.fs",
    "chars": 24904,
    "preview": "namespace Grace.Actors\n\nopen Azure.Storage.Blobs\nopen Azure.Storage.Blobs.Specialized\nopen DiffPlex\nopen DiffPlex.DiffBu"
  },
  {
    "path": "src/Grace.Actors/DirectoryAppearance.Actor.fs",
    "chars": 2913,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/DirectoryVersion.Actor.fs",
    "chars": 63044,
    "preview": "namespace Grace.Actors\n\nopen Azure.Storage.Blobs\nopen Azure.Storage.Blobs.Models\nopen Azure.Storage.Blobs.Specialized\nop"
  },
  {
    "path": "src/Grace.Actors/Extensions/MemoryCache.Extensions.Actor.fs",
    "chars": 12625,
    "preview": "namespace Grace.Actors.Extensions\n\nopen Grace.Shared.Constants\nopen Grace.Types.Types\nopen Orleans.Runtime\nopen Microsof"
  },
  {
    "path": "src/Grace.Actors/FileAppearance.Actor.fs",
    "chars": 2441,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/GlobalLock.Actor.fs",
    "chars": 2107,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.MemoryCache\no"
  },
  {
    "path": "src/Grace.Actors/Grace.Actors.fsproj",
    "chars": 4910,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net10.0</TargetFramework>\n        <LangV"
  },
  {
    "path": "src/Grace.Actors/GrainRepository.Actor.fs",
    "chars": 1301,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.MemoryCache\no"
  },
  {
    "path": "src/Grace.Actors/Interfaces.Actor.fs",
    "chars": 28267,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Types\nopen Grace.Shared\nopen Grace.Types.Authorization\nopen Grace.Types.Branch"
  },
  {
    "path": "src/Grace.Actors/NamedSection.Actor.fs",
    "chars": 120,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\n\nmodule NamedSection =\n\n    let ActorName = ActorName.NamedSection\n"
  },
  {
    "path": "src/Grace.Actors/Organization.Actor.fs",
    "chars": 17593,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/OrganizationName.Actor.fs",
    "chars": 1603,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.MemoryCache\no"
  },
  {
    "path": "src/Grace.Actors/Owner.Actor.fs",
    "chars": 18826,
    "preview": "namespace Grace.Actors\n\nopen FSharp.Control\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Exte"
  },
  {
    "path": "src/Grace.Actors/OwnerName.Actor.fs",
    "chars": 1508,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.MemoryCache\no"
  },
  {
    "path": "src/Grace.Actors/PersonalAccessToken.Actor.fs",
    "chars": 8789,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Interfaces\nopen Grace.Sh"
  },
  {
    "path": "src/Grace.Actors/Policy.Actor.fs",
    "chars": 8871,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/PromotionQueue.Actor.fs",
    "chars": 8515,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/PromotionSet.Actor.fs",
    "chars": 104692,
    "preview": "namespace Grace.Actors\n\nopen Azure.Storage.Blobs\nopen Azure.Storage.Blobs.Specialized\nopen Grace.Actors.Constants\nopen G"
  },
  {
    "path": "src/Grace.Actors/Reference.Actor.fs",
    "chars": 19499,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/Reminder.Actor.fs",
    "chars": 9880,
    "preview": "namespace Grace.Actors\n\nopen Orleans\nopen Orleans.Runtime\nopen Grace.Actors\nopen Grace.Actors.Constants\nopen Grace.Actor"
  },
  {
    "path": "src/Grace.Actors/Repository.Actor.fs",
    "chars": 30317,
    "preview": "namespace Grace.Actors\n\nopen FSharp.Control\nopen FSharpPlus\nopen Grace.Actors.Constants\nopen Grace.Actors.Interfaces\nope"
  },
  {
    "path": "src/Grace.Actors/Repository.Actor.fs (ApplyEvent Method)",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/Grace.Actors/RepositoryName.Actor.fs",
    "chars": 1369,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.MemoryCache\no"
  },
  {
    "path": "src/Grace.Actors/RepositoryPermission.Actor.fs",
    "chars": 4629,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Interfaces\nopen Grace.Sh"
  },
  {
    "path": "src/Grace.Actors/Review.Actor.fs",
    "chars": 10557,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/Services.Actor.fs",
    "chars": 149513,
    "preview": "namespace Grace.Actors\n\nopen Azure.Core\nopen Azure.Identity\nopen Azure.Messaging.ServiceBus\nopen Azure.Storage\nopen Azur"
  },
  {
    "path": "src/Grace.Actors/Timing.Actor.fs",
    "chars": 4298,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Context\nopen Grace.Actors.Types\nopen Grace.Types.Types\nopen Grace.Shared.Utili"
  },
  {
    "path": "src/Grace.Actors/Types.Actor.fs",
    "chars": 928,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Types.Types\nopen Grace.Shared.Utilities\nopen NodaTime\nope"
  },
  {
    "path": "src/Grace.Actors/User.Actor.fs",
    "chars": 953,
    "preview": "namespace Grace.Actors\n\nopen Grace.Shared\nopen Grace.Shared.Constants\nopen Grace.Shared.Utilities\nopen NodaTime\nopen Sys"
  },
  {
    "path": "src/Grace.Actors/ValidationResult.Actor.fs",
    "chars": 6210,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Interfaces\nopen Grace.Ac"
  },
  {
    "path": "src/Grace.Actors/ValidationSet.Actor.fs",
    "chars": 6572,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Interfaces\nopen Grace.Ac"
  },
  {
    "path": "src/Grace.Actors/WorkItem.Actor.fs",
    "chars": 9354,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Extensions.ActorProxy\nop"
  },
  {
    "path": "src/Grace.Actors/WorkItemNumber.Actor.fs",
    "chars": 1611,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Interfaces\nopen Grace.Ac"
  },
  {
    "path": "src/Grace.Actors/WorkItemNumberCounter.Actor.fs",
    "chars": 2023,
    "preview": "namespace Grace.Actors\n\nopen Grace.Actors.Constants\nopen Grace.Actors.Context\nopen Grace.Actors.Interfaces\nopen Grace.Ac"
  },
  {
    "path": "src/Grace.Aspire.AppHost/AGENTS.md",
    "chars": 506,
    "preview": "# Grace.Aspire.AppHost Agent Notes\n\n- AppHost reads the Grace.Server user-secrets ID and forwards selected auth\n  settin"
  },
  {
    "path": "src/Grace.Aspire.AppHost/Grace.Aspire.AppHost.csproj",
    "chars": 1156,
    "preview": "<Project Sdk=\"Aspire.AppHost.Sdk/13.1.0\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net10"
  },
  {
    "path": "src/Grace.Aspire.AppHost/Program.Aspire.AppHost.cs",
    "chars": 39087,
    "preview": "extern alias Shared;\n\nusing Aspire.Hosting;\nusing Aspire.Hosting.ApplicationModel;\nusing Aspire.Hosting.Azure;\nusing Asp"
  },
  {
    "path": "src/Grace.Aspire.AppHost/Properties/launchSettings.json",
    "chars": 2011,
    "preview": "{\n  \"profiles\": {\n    \"http\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"environmentVariables"
  },
  {
    "path": "src/Grace.Aspire.AppHost/appsettings.json",
    "chars": 251,
    "preview": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft\": \"Warning\"\n    }\n  },\n  \"Aspire\": {"
  },
  {
    "path": "src/Grace.Aspire.ServiceDefaults/Extensions.cs",
    "chars": 4336,
    "preview": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Diagnostics.HealthChecks;\nusing Microsoft.Extensions.Depe"
  },
  {
    "path": "src/Grace.Aspire.ServiceDefaults/Grace.Aspire.ServiceDefaults.csproj",
    "chars": 1325,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <OutputType>Library</OutputType>\n        <TargetFramework"
  },
  {
    "path": "src/Grace.Authorization.Tests/AuthorizationSemantics.Tests.fs",
    "chars": 7845,
    "preview": "namespace Grace.Authorization.Tests\n\nopen FsCheck\nopen Grace.Shared.Authorization\nopen Grace.Shared.Utilities\nopen Grace"
  },
  {
    "path": "src/Grace.Authorization.Tests/ClaimMapping.Tests.fs",
    "chars": 3216,
    "preview": "namespace Grace.Authorization.Tests\n\nopen Grace.Server.Security\nopen Microsoft.AspNetCore.Authentication\nopen Microsoft."
  },
  {
    "path": "src/Grace.Authorization.Tests/EndpointAuthorizationManifest.Tests.fs",
    "chars": 3688,
    "preview": "namespace Grace.Authorization.Tests\n\nopen Grace.Server.Security.EndpointAuthorizationManifest\nopen NUnit.Framework\nopen "
  },
  {
    "path": "src/Grace.Authorization.Tests/Grace.Authorization.Tests.fsproj",
    "chars": 1949,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net10.0</TargetFramework>\n    <LangVersion>pr"
  },
  {
    "path": "src/Grace.Authorization.Tests/PathPermissions.Tests.fs",
    "chars": 2404,
    "preview": "namespace Grace.Authorization.Tests\n\nopen Grace.Shared.Authorization\nopen Grace.Shared.Utilities\nopen Grace.Types.Author"
  },
  {
    "path": "src/Grace.Authorization.Tests/PermissionEvaluator.Tests.fs",
    "chars": 4453,
    "preview": "namespace Grace.Authorization.Tests\n\nopen Grace.Server.Security\nopen Grace.Shared.Authorization\nopen Grace.Shared.Utilit"
  },
  {
    "path": "src/Grace.Authorization.Tests/PersonalAccessToken.Tests.fs",
    "chars": 2189,
    "preview": "namespace Grace.Authorization.Tests\n\nopen FsCheck\nopen Grace.Types.PersonalAccessToken\nopen NUnit.Framework\nopen System\n"
  },
  {
    "path": "src/Grace.Authorization.Tests/Program.fs",
    "chars": 47,
    "preview": "module Program\n\n[<EntryPoint>]\nlet main _ = 0\n"
  },
  {
    "path": "src/Grace.CLI/AGENTS.md",
    "chars": 3397,
    "preview": "# Grace.CLI Agents Guide\n\nRead `../AGENTS.md` for global expectations before updating CLI code.\n\n## Purpose\n\n- Provide d"
  },
  {
    "path": "src/Grace.CLI/Command/Access.CLI.fs",
    "chars": 41761,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Common.Validations\nopen Grace.CLI.Serv"
  },
  {
    "path": "src/Grace.CLI/Command/Admin.CLI.fs",
    "chars": 31576,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Common.Validations\nopen Grace.CLI.Serv"
  },
  {
    "path": "src/Grace.CLI/Command/Agent.CLI.fs",
    "chars": 60951,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace.SDK\nopen Grace"
  },
  {
    "path": "src/Grace.CLI/Command/Auth.CLI.fs",
    "chars": 63693,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace.SDK\nopen Grace"
  },
  {
    "path": "src/Grace.CLI/Command/Branch.CLI.fs",
    "chars": 232659,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grac"
  },
  {
    "path": "src/Grace.CLI/Command/Candidate.CLI.fs",
    "chars": 19782,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace.SDK\nopen Grace"
  },
  {
    "path": "src/Grace.CLI/Command/Common.CLI.fs",
    "chars": 20711,
    "preview": "namespace Grace.CLI\n\nopen FSharpPlus\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace.Shared\nopen Grace.Shared.Val"
  },
  {
    "path": "src/Grace.CLI/Command/Config.CLI.fs",
    "chars": 8963,
    "preview": "namespace Grace.CLI.Command\n\nopen DiffPlex\nopen DiffPlex.DiffBuilder.Model\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Gr"
  },
  {
    "path": "src/Grace.CLI/Command/Connect.CLI.fs",
    "chars": 38885,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grac"
  },
  {
    "path": "src/Grace.CLI/Command/Diff.CLI.fs",
    "chars": 45214,
    "preview": "namespace Grace.CLI.Command\n\nopen DiffPlex\nopen DiffPlex.DiffBuilder.Model\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Gr"
  },
  {
    "path": "src/Grace.CLI/Command/DirectoryVersion.CLI.fs",
    "chars": 8891,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Common.Validations\nopen Grace.CLI.Serv"
  },
  {
    "path": "src/Grace.CLI/Command/History.CLI.fs",
    "chars": 32887,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI\nopen Grace.CLI.Common\nopen Grace.CLI.Text\nopen Grace.Shared\nopen Grace.Share"
  },
  {
    "path": "src/Grace.CLI/Command/Maintenance.CLI.fs",
    "chars": 44335,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace.SDK\nopen Grace"
  },
  {
    "path": "src/Grace.CLI/Command/Organization.CLI.fs",
    "chars": 31474,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Common.Validations\nopen Grace.CLI.Serv"
  },
  {
    "path": "src/Grace.CLI/Command/Owner.CLI.fs",
    "chars": 29499,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Common.Validations\nopen Grace.CLI.Text"
  },
  {
    "path": "src/Grace.CLI/Command/PromotionSet.CLI.fs",
    "chars": 44224,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace.SDK\nopen Grace"
  },
  {
    "path": "src/Grace.CLI/Command/Queue.CLI.fs",
    "chars": 24038,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace.SDK\nopen Grace"
  },
  {
    "path": "src/Grace.CLI/Command/Reference.CLI.fs",
    "chars": 75645,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Common.Validations\nopen Grace.CLI.Serv"
  },
  {
    "path": "src/Grace.CLI/Command/Repository.CLI.fs",
    "chars": 112066,
    "preview": "namespace Grace.CLI.Command\n\nopen FSharpPlus\nopen Grace.CLI.Common\nopen Grace.CLI.Common.Validations\nopen Grace.CLI.Serv"
  },
  {
    "path": "src/Grace.CLI/Command/Review.CLI.fs",
    "chars": 34143,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace.SDK\nopen Grace"
  },
  {
    "path": "src/Grace.CLI/Command/Services.CLI.fs",
    "chars": 95108,
    "preview": "namespace Grace.CLI\n\nopen Microsoft.Extensions\nopen FSharp.Collections\nopen Grace.CLI.Text\nopen Grace.SDK\nopen Grace.Sha"
  },
  {
    "path": "src/Grace.CLI/Command/Watch.CLI.fs",
    "chars": 44113,
    "preview": "namespace Grace.CLI.Command\n\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.SDK\nopen Grace.SDK.Common\nopen Gra"
  },
  {
    "path": "src/Grace.CLI/Command/WorkItem.CLI.fs",
    "chars": 59575,
    "preview": "namespace Grace.CLI.Command\n\nopen Azure.Storage.Blobs\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\n"
  },
  {
    "path": "src/Grace.CLI/Conversion.md",
    "chars": 9683,
    "preview": "# Grace CLI Command Conversion Guide\n\nThis note bundles the shared knowledge needed to migrate legacy CLI subcommands fr"
  },
  {
    "path": "src/Grace.CLI/Grace.CLI.fsproj",
    "chars": 4997,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project Sdk=\"Microsoft.NET.Sdk\">\n\t<PropertyGroup>\n\t\t<TargetFramework>net10.0</Ta"
  },
  {
    "path": "src/Grace.CLI/HistoryStorage.CLI.fs",
    "chars": 23427,
    "preview": "namespace Grace.CLI\n\nopen Grace.CLI.Text\nopen Grace.Shared\nopen Grace.Shared.Client\nopen Grace.Shared.Utilities\nopen Nod"
  },
  {
    "path": "src/Grace.CLI/LocalStateDb.CLI.fs",
    "chars": 57004,
    "preview": "namespace Grace.CLI\n\nopen System\nopen System.Collections.Concurrent\nopen System.Collections.Generic\nopen System.Diagnost"
  },
  {
    "path": "src/Grace.CLI/Log.CLI.fs",
    "chars": 712,
    "preview": "namespace Grace.CLI\n\nopen Grace.Shared\nopen Grace.Types.Types\nopen Grace.Shared.Utilities\nopen NodaTime\nopen System\nopen"
  },
  {
    "path": "src/Grace.CLI/Program.CLI.fs",
    "chars": 48268,
    "preview": "namespace Grace.CLI\n\nopen Grace.CLI.Command\nopen Grace.CLI.Common\nopen Grace.CLI.Services\nopen Grace.CLI.Text\nopen Grace"
  },
  {
    "path": "src/Grace.CLI/Properties/launchSettings.json",
    "chars": 3753,
    "preview": "{\n  \"profiles\": {\n    \"WSL\": {\n      \"commandName\": \"WSL2\",\n      \"distributionName\": \"\"\n    },\n    \"grace diff commit\":"
  },
  {
    "path": "src/Grace.CLI/Text.CLI.fs",
    "chars": 7145,
    "preview": "namespace Grace.CLI\n\nopen Grace.Shared.Resources.Utilities\nopen Grace.Shared.Resources.Text\n\nmodule Text =\n\n    module O"
  },
  {
    "path": "src/Grace.CLI.LocalStateDb.Worker/Grace.CLI.LocalStateDb.Worker.fsproj",
    "chars": 571,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net10.0</Targe"
  },
  {
    "path": "src/Grace.CLI.LocalStateDb.Worker/Program.fs",
    "chars": 1697,
    "preview": "namespace Grace.CLI.LocalStateDb.Worker\n\nopen System\nopen System.Threading.Tasks\nopen Grace.CLI\nopen Grace.Types.Types\no"
  },
  {
    "path": "src/Grace.CLI.Tests/AGENTS.md",
    "chars": 1360,
    "preview": "# Grace.CLI.Tests Agents Guide\n\nRead `../AGENTS.md` for global expectations before updating CLI tests.\n\n## Purpose\n\n- Va"
  },
  {
    "path": "src/Grace.CLI.Tests/Agent.CLI.Tests.fs",
    "chars": 9959,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen Grace.Shared.Client.Configuration\nopen NUnit.Framework\nopen S"
  },
  {
    "path": "src/Grace.CLI.Tests/Auth.Tests.fs",
    "chars": 2729,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI.Command\nopen Grace.Shared\nopen Grace.Types\nopen NUnit.Framework\nop"
  },
  {
    "path": "src/Grace.CLI.Tests/AuthTokenBundle.Tests.fs",
    "chars": 1084,
    "preview": "namespace Grace.CLI.Tests\n\nopen Grace.CLI.Command\nopen Grace.Shared\nopen NodaTime\nopen NUnit.Framework\nopen System.Text."
  },
  {
    "path": "src/Grace.CLI.Tests/Connect.CLI.Tests.fs",
    "chars": 5898,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen Grace.CLI.Command\nopen Grace.CLI.Text\nopen Grace.Shared.Clien"
  },
  {
    "path": "src/Grace.CLI.Tests/Grace.CLI.Tests.fsproj",
    "chars": 2536,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\t<PropertyGroup>\n\t\t<TargetFramework>net10.0</TargetFramework>\n\t\t<PlatformTarget>x64</P"
  },
  {
    "path": "src/Grace.CLI.Tests/History.CLI.Tests.fs",
    "chars": 2124,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen Grace.CLI.Command\nopen Grace.Shared.Utilities\nopen NodaTime\no"
  },
  {
    "path": "src/Grace.CLI.Tests/HistoryStorage.CLI.Tests.fs",
    "chars": 8322,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen Grace.Shared\nopen Grace.Shared.Client\nopen Grace.Shared.Utili"
  },
  {
    "path": "src/Grace.CLI.Tests/LocalStateDb.Tests.fs",
    "chars": 72018,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen Grace.Shared.Client.Configuration\nopen Grace.Shared\nopen Grac"
  },
  {
    "path": "src/Grace.CLI.Tests/Program.CLI.Tests.fs",
    "chars": 15962,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen NUnit.Framework\nopen System\n\n[<TestFixture>]\nmodule CommandPa"
  },
  {
    "path": "src/Grace.CLI.Tests/Program.fs",
    "chars": 82,
    "preview": "namespace Grace.CLI.Tests\n\nmodule Program =\n    [<EntryPoint>]\n    let main _ = 0\n"
  },
  {
    "path": "src/Grace.CLI.Tests/PromotionSet.CLI.Tests.fs",
    "chars": 9494,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen NUnit.Framework\nopen System\nopen System.IO\n\n[<NonParallelizab"
  },
  {
    "path": "src/Grace.CLI.Tests/Queue.CLI.Tests.fs",
    "chars": 3335,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen NUnit.Framework\nopen System\n\n[<NonParallelizable>]\nmodule Que"
  },
  {
    "path": "src/Grace.CLI.Tests/Review.CLI.Tests.fs",
    "chars": 15116,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen Grace.CLI.Command\nopen Grace.SDK\nopen Grace.Shared\nopen Grace"
  },
  {
    "path": "src/Grace.CLI.Tests/Watch.Tests.fs",
    "chars": 5168,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsUnit\nopen Grace.CLI\nopen Grace.CLI.Command\nopen Grace.Shared\nopen Grace.Shared.Client."
  },
  {
    "path": "src/Grace.CLI.Tests/WorkItem.CLI.Tests.fs",
    "chars": 15437,
    "preview": "namespace Grace.CLI.Tests\n\nopen FsCheck.NUnit\nopen FsUnit\nopen Grace.CLI\nopen NUnit.Framework\nopen System\nopen System.Co"
  },
  {
    "path": "src/Grace.Load/Grace.Load.fsproj",
    "chars": 1088,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net10.0</TargetFr"
  },
  {
    "path": "src/Grace.Load/Program.Load.fs",
    "chars": 30848,
    "preview": "namespace Grace\n\nopen Grace.SDK\nopen Grace.Shared\nopen Grace.Shared.Parameters\nopen Grace.Types.Types\nopen Grace.Shared."
  },
  {
    "path": "src/Grace.Load/Properties/launchSettings.json",
    "chars": 209,
    "preview": "{\n  \"profiles\": {\n    \"WSL\": {\n      \"commandName\": \"WSL2\",\n      \"distributionName\": \"\"\n    },\n    \"graceload\": {\n     "
  },
  {
    "path": "src/Grace.Orleans.CodeGen/Declaration.Orleans.CodeGen.cs",
    "chars": 560,
    "preview": "// The GenerateCodeForDeclaringAssembly attribute finds all .NET types in the assembly that contains the type you specif"
  },
  {
    "path": "src/Grace.Orleans.CodeGen/Grace.Orleans.CodeGen.csproj",
    "chars": 1042,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net10.0</TargetFramework>\n\t\t<ImplicitUsings>enab"
  },
  {
    "path": "src/Grace.Orleans.CodeGen/instructions.md",
    "chars": 562,
    "preview": "# Grace.Orleans.CodeGen — instructions.md\n\nSee repository-wide guidance in `../agents.md`. The sections below are proje"
  },
  {
    "path": "src/Grace.SDK/AGENTS.md",
    "chars": 1892,
    "preview": "# Grace.SDK Agents Guide\n\nConsult `../AGENTS.md` for global policies before modifying the SDK.\n\n## Purpose\n\n- Offer stab"
  },
  {
    "path": "src/Grace.SDK/Access.SDK.fs",
    "chars": 2543,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Access\nopen Grace.Shared.Parameters.Common\nopen "
  },
  {
    "path": "src/Grace.SDK/Admin.SDK.fs",
    "chars": 2680,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Reminder\nopen Grace.Shared.Utilities\nopen Grace."
  },
  {
    "path": "src/Grace.SDK/Artifact.SDK.fs",
    "chars": 1390,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Artifact\nopen Grace.Types.Artifact\nopen System\n\n"
  },
  {
    "path": "src/Grace.SDK/Auth.SDK.fs",
    "chars": 2843,
    "preview": "namespace Grace.SDK\n\nopen Grace.Shared\nopen Grace.Shared.Services\nopen Grace.Shared.Utilities\nopen Grace.Types.Auth\nopen"
  },
  {
    "path": "src/Grace.SDK/Branch.SDK.fs",
    "chars": 10428,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Branch\nopen Grace.Shared.Utilities\nopen Grace.Ty"
  },
  {
    "path": "src/Grace.SDK/Common.SDK.fs",
    "chars": 9211,
    "preview": "namespace Grace.SDK\n\nopen Grace.Shared\nopen Grace.Shared.Client.Configuration\nopen Grace.Shared.Services\nopen Grace.Type"
  },
  {
    "path": "src/Grace.SDK/Diff.SDK.fs",
    "chars": 1020,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared\nopen Grace.Types.Diff\nopen Grace.Shared.Parameters.Diff\nope"
  },
  {
    "path": "src/Grace.SDK/DirectoryVersion.SDK.fs",
    "chars": 2546,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.DirectoryVersion\nopen Grace.Shared\nopen Grace.Ty"
  },
  {
    "path": "src/Grace.SDK/Grace.SDK.fsproj",
    "chars": 2276,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net10.0</TargetFramework>\n\t\t<LangVersion>preview<"
  },
  {
    "path": "src/Grace.SDK/Organization.SDK.fs",
    "chars": 3446,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Types.Organization\nopen Grace.Shared.Parameters.Organization\nopen "
  },
  {
    "path": "src/Grace.SDK/Owner.SDK.fs",
    "chars": 3094,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Owner\nopen Grace.Types.Owner\nopen Grace.Types.Ty"
  },
  {
    "path": "src/Grace.SDK/PersonalAccessToken.SDK.fs",
    "chars": 3766,
    "preview": "namespace Grace.SDK\n\nopen Grace.Shared\nopen Grace.Shared.Constants\nopen Grace.Shared.Parameters.Common\nopen Grace.Shared"
  },
  {
    "path": "src/Grace.SDK/Policy.SDK.fs",
    "chars": 749,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Policy\nopen Grace.Types.Policy\nopen System.Threa"
  },
  {
    "path": "src/Grace.SDK/PromotionSet.SDK.fs",
    "chars": 2452,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.PromotionSet\nopen Grace.Types.PromotionSet\nopen "
  },
  {
    "path": "src/Grace.SDK/Queue.SDK.fs",
    "chars": 1397,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Queue\nopen Grace.Types.Queue\nopen System.Threadi"
  },
  {
    "path": "src/Grace.SDK/Repository.SDK.fs",
    "chars": 9051,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Repository\nopen Grace.Shared.Utilities\nopen Grac"
  },
  {
    "path": "src/Grace.SDK/Review.SDK.fs",
    "chars": 5399,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Review\nopen Grace.Types.Review\nopen System\nopen "
  },
  {
    "path": "src/Grace.SDK/Storage.SDK.fs",
    "chars": 22074,
    "preview": "namespace Grace.SDK\n\nopen Azure.Core.Pipeline\nopen Azure.Storage\nopen Azure.Storage.Blobs\nopen Azure.Storage.Blobs.Model"
  },
  {
    "path": "src/Grace.SDK/ValidationResult.SDK.fs",
    "chars": 447,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Validation\nopen Grace.Types.Validation\n\n/// Clie"
  },
  {
    "path": "src/Grace.SDK/ValidationSet.SDK.fs",
    "chars": 1135,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.Validation\nopen Grace.Types.Validation\n\n/// Clie"
  },
  {
    "path": "src/Grace.SDK/WorkItem.SDK.fs",
    "chars": 4277,
    "preview": "namespace Grace.SDK\n\nopen Grace.SDK.Common\nopen Grace.Shared.Parameters.WorkItem\nopen Grace.Types.WorkItem\nopen System.T"
  },
  {
    "path": "src/Grace.Server/AGENTS.md",
    "chars": 2315,
    "preview": "# Grace.Server Agents Guide\n\nGlobal policies live in `../AGENTS.md`. Review them before touching server\ncode.\n\n## Purpos"
  },
  {
    "path": "src/Grace.Server/Access.Server.fs",
    "chars": 29227,
    "preview": "namespace Grace.Server\n\nopen Giraffe\nopen Grace.Actors\nopen Grace.Actors.Extensions\nopen Grace.Server.Security\nopen Grac"
  },
  {
    "path": "src/Grace.Server/ApplicationContext.Server.fs",
    "chars": 14187,
    "preview": "namespace Grace.Server\n\nopen Azure.Core\nopen Azure.Identity\nopen Azure.Storage\nopen Azure.Storage.Blobs\nopen Grace.Actor"
  },
  {
    "path": "src/Grace.Server/Artifact.Server.fs",
    "chars": 10392,
    "preview": "namespace Grace.Server\n\nopen Giraffe\nopen Grace.Actors.Extensions.ActorProxy\nopen Grace.Actors.Services\nopen Grace.Serve"
  }
]

// ... and 189 more files (download for full content)

About this extraction

This page contains the full source code of the ScottArbeit/Grace GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 389 files (4.6 MB), approximately 1.2M tokens, and a symbol index with 17 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!