-
Notifications
You must be signed in to change notification settings - Fork 327
Settings schema v2 (parallel to legacy) #1051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Soph
wants to merge
8
commits into
main
Choose a base branch
from
soph/settings-v2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7a6c50b
Add settings schema v2 type definitions
Soph 39b84a5
Add LoadV2 with legacy synthesizer
Soph c532606
Simplify LoadV2 after review
Soph db7f185
Fix nested partial override, mixed shapes, and add Validate
Soph f94e677
Address PR feedback on schema v2
Soph bd79893
Rename checkpoints.remote → checkpoints.git
Soph 1f5ebef
Add testdata examples for schema v2
Soph bee8422
Extract modelSonnet constant in settings tests
Soph File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| // Schema-v2 settings types. See synthesizeFromLegacy in load_v2.go for | ||
| // the v1→v2 mapping. | ||
|
|
||
| package settings | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "sort" | ||
| "strings" | ||
| ) | ||
|
|
||
| // CurrentSchemaVersion is the schema version emitted by v2 writers. | ||
| // Files marked with this version are parsed via the v2 path; older files | ||
| // are loaded via the legacy parser and synthesized into Settings on the fly. | ||
| const CurrentSchemaVersion = 2 | ||
|
|
||
| // Settings is the schema-v2 representation of .entire/settings.json. | ||
| // | ||
| // All fields are JSON-omitempty where defaults are well-defined so that | ||
| // hand-written settings files stay minimal. Pointer types are used where | ||
| // "unset" must be distinguishable from "explicit false" (e.g. Telemetry, | ||
| // SignCommits, PushSessions). | ||
| type Settings struct { | ||
| // Schema identifies the settings file schema version. Always 2 in this struct. | ||
| Schema int `json:"schema"` | ||
|
|
||
| // Enabled indicates whether Entire is active. When false, CLI commands | ||
| // show a disabled message and hooks exit silently. Defaults to true. | ||
| Enabled bool `json:"enabled"` | ||
|
|
||
| // LocalDev indicates whether to use "go run" instead of the "entire" | ||
| // binary. Used during development when the binary is not installed. | ||
| LocalDev bool `json:"local_dev,omitempty"` | ||
|
|
||
| // Logging configures runtime logging. | ||
| Logging LoggingConfig `json:"logging,omitempty"` | ||
|
|
||
| // Checkpoints configures permanent checkpoint storage and related ops. | ||
| Checkpoints CheckpointsConfig `json:"checkpoints,omitempty"` | ||
|
|
||
| // Hooks configures git hook behavior. | ||
| Hooks HooksConfig `json:"hooks,omitempty"` | ||
|
|
||
| // Features toggles optional behaviors. | ||
| Features FeaturesConfig `json:"features,omitempty"` | ||
|
|
||
| // Redaction configures PII redaction beyond the default secret detection. | ||
| Redaction *RedactionSettings `json:"redaction,omitempty"` | ||
|
|
||
| // Telemetry controls anonymous usage analytics. | ||
| // nil = not asked yet, true = opted in, false = opted out. | ||
| Telemetry *bool `json:"telemetry,omitempty"` | ||
|
|
||
| // SummaryGeneration configures provider selection and timeout for | ||
| // `entire explain --generate`. | ||
| SummaryGeneration *SummaryGenerationConfig `json:"summary_generation,omitempty"` | ||
| } | ||
|
|
||
| // LoggingConfig configures runtime logging verbosity. | ||
| type LoggingConfig struct { | ||
| // Level is the logging verbosity (debug, info, warn, error). | ||
| // Can be overridden by ENTIRE_LOG_LEVEL. Defaults to "info" when unset. | ||
| Level string `json:"level,omitempty"` | ||
| } | ||
|
|
||
| // CheckpointsConfig configures checkpoint storage and related operations. | ||
| // | ||
| // Primary serves all reads and is the authoritative writer. Mirrors receive | ||
| // best-effort fan-out writes (warn on failure, never serve reads). This | ||
| // shape replaces the legacy strategy_options{checkpoints_version, gmeta, ...} | ||
| // soup with explicit, typed selection. | ||
| type CheckpointsConfig struct { | ||
| // Primary is the authoritative checkpoint backend. | ||
| // Reads always come from Primary. Writes that fail here are fatal. | ||
| Primary BackendConfig `json:"primary"` | ||
|
|
||
| // Mirrors are best-effort write targets. | ||
| // Mirror failures are logged but do not fail the operation. Mirrors | ||
| // never serve reads — they are export targets, not sources of truth. | ||
| Mirrors []BackendConfig `json:"mirrors,omitempty"` | ||
|
|
||
| // Git configures the destination for git-based backends (v1, v2, gmeta). | ||
| // Optional; when unset, the default origin is used. Per-backend overrides | ||
| // can be added by giving BackendConfig its own Git field if a future use | ||
| // case needs different destinations for different git backends — today | ||
| // this single value applies to all of them. | ||
| Git *CheckpointRemoteConfig `json:"git,omitempty"` | ||
|
|
||
| // FullTranscriptRetentionDays is the retention window (in days) for | ||
| // archived raw-transcript generations. Zero/negative falls back to | ||
| // the documented default (60 days). | ||
| FullTranscriptRetentionDays int `json:"full_transcript_retention_days,omitempty"` | ||
|
|
||
| // SignCommits controls whether checkpoint commits are signed. | ||
| // nil/true = sign (default), false = skip signing. | ||
| SignCommits *bool `json:"sign_commits,omitempty"` | ||
|
|
||
| // FilteredFetches enables --filter=blob:none on checkpoint fetches. | ||
| FilteredFetches bool `json:"filtered_fetches,omitempty"` | ||
|
|
||
| // PushSessions controls whether session refs are pushed to remotes. | ||
| // nil = push (default), explicit false = do not push. | ||
| PushSessions *bool `json:"push_sessions,omitempty"` | ||
| } | ||
|
|
||
| // BackendConfig identifies and configures a single checkpoint backend. | ||
| // | ||
| // The Type field selects the backend implementation (e.g. "v1", "v2", | ||
| // "gmeta"). Backend-specific configuration is added as additional fields | ||
| // here as backends are introduced (e.g. an "s3" backend would carry | ||
| // bucket/region; today every supported backend needs only Type). | ||
| type BackendConfig struct { | ||
| // Type is the backend identifier. Recognized values: "v1", "v2", "gmeta". | ||
| Type string `json:"type"` | ||
| } | ||
|
|
||
| // HooksConfig configures git hook behavior. | ||
| type HooksConfig struct { | ||
| // CommitLinking controls how commits are linked to agent sessions. | ||
| // "always" = auto-link without prompting, "prompt" = ask each commit. | ||
| // Defaults to "prompt" when unset. | ||
| CommitLinking string `json:"commit_linking,omitempty"` | ||
|
|
||
| // AbsoluteGitHookPath embeds the full binary path in git hooks instead | ||
| // of bare "entire". Needed for GUI git clients (Xcode, Tower, etc.) | ||
| // that don't source shell profiles and can't find "entire" on PATH. | ||
| AbsoluteGitHookPath bool `json:"absolute_git_hook_path,omitempty"` | ||
| } | ||
|
|
||
| // FeaturesConfig toggles optional product behaviors. | ||
| type FeaturesConfig struct { | ||
| // Summarize enables AI-generated checkpoint summaries. | ||
| Summarize bool `json:"summarize,omitempty"` | ||
|
|
||
| // ExternalAgents enables discovery and registration of external agent | ||
| // plugins (entire-agent-* binaries on $PATH). | ||
| ExternalAgents bool `json:"external_agents,omitempty"` | ||
|
|
||
| // Vercel marks the repository as using Vercel; the metadata branch | ||
| // then includes a vercel.json that disables deployments for Entire branches. | ||
| Vercel bool `json:"vercel,omitempty"` | ||
| } | ||
|
|
||
| // SummaryGenerationConfig configures `entire explain --generate`. | ||
| // | ||
| // Replaces legacy SummaryGenerationSettings + the top-level | ||
| // SummaryTimeoutSeconds field, grouping all summary-generation knobs. | ||
| type SummaryGenerationConfig struct { | ||
| // Provider is the agent name for summary generation | ||
| // (e.g. "claude-code", "codex", "gemini"). | ||
| Provider string `json:"provider,omitempty"` | ||
|
|
||
| // Model is an optional model hint passed to the selected provider. | ||
| Model string `json:"model,omitempty"` | ||
|
|
||
| // TimeoutSeconds is an optional hard deadline for summary generation. | ||
| // Zero or negative means "unset" — the caller picks the default. | ||
| TimeoutSeconds int `json:"timeout_seconds,omitempty"` | ||
| } | ||
|
|
||
| // knownBackendTypes is the set of backend type identifiers Validate accepts. | ||
| // Kept here next to BackendConfig so adding a new backend type is a single | ||
| // place to update — Validate's error messages format from this map. | ||
| var knownBackendTypes = map[string]struct{}{ | ||
| BackendTypeV1: {}, | ||
| BackendTypeV2: {}, | ||
| BackendTypeGmeta: {}, | ||
| } | ||
|
|
||
| // knownBackendTypesList returns the backend types in sorted order for use | ||
| // in error messages. Sorting keeps test assertions deterministic. | ||
| func knownBackendTypesList() string { | ||
| names := make([]string, 0, len(knownBackendTypes)) | ||
| for n := range knownBackendTypes { | ||
| names = append(names, n) | ||
| } | ||
| sort.Strings(names) | ||
| return strings.Join(names, ", ") | ||
| } | ||
|
|
||
| // Validate checks the Settings for semantic correctness beyond what JSON | ||
| // decoding catches. Run this after parsing or merging to surface invalid | ||
| // configurations at load time rather than at first use. | ||
| // | ||
| // Current rules: | ||
| // - Schema must be exactly CurrentSchemaVersion. Writers emit only the | ||
| // current value, and Validate rejects others. (isSchemaV2 accepts >= | ||
| // as a shape probe, but strict decode plus this check enforce the | ||
| // actual contract.) | ||
| // - Checkpoints.Primary.Type must be a known backend. | ||
| // - Each Mirror's Type must be a known backend. | ||
| // - SummaryGeneration.Model requires SummaryGeneration.Provider, matching | ||
| // the legacy SummaryGenerationSettings.Validate semantics. | ||
| func (s *Settings) Validate() error { | ||
| if s == nil { | ||
| return errors.New("settings: nil") | ||
| } | ||
| if s.Schema != CurrentSchemaVersion { | ||
| return fmt.Errorf("settings: schema = %d, want %d", s.Schema, CurrentSchemaVersion) | ||
| } | ||
| if _, ok := knownBackendTypes[s.Checkpoints.Primary.Type]; !ok { | ||
| return fmt.Errorf("checkpoints.primary.type = %q: must be one of %s", s.Checkpoints.Primary.Type, knownBackendTypesList()) | ||
| } | ||
| for i, m := range s.Checkpoints.Mirrors { | ||
| if _, ok := knownBackendTypes[m.Type]; !ok { | ||
| return fmt.Errorf("checkpoints.mirrors[%d].type = %q: must be one of %s", i, m.Type, knownBackendTypesList()) | ||
| } | ||
| } | ||
| if s.SummaryGeneration != nil && s.SummaryGeneration.Model != "" && s.SummaryGeneration.Provider == "" { | ||
| return fmt.Errorf("summary_generation.model %q set without summary_generation.provider", s.SummaryGeneration.Model) | ||
| } | ||
| return nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Settings testdata | ||
|
|
||
| Canonical example settings.json files. Loaded by `TestLoadV2_TestdataExamples` | ||
| in `load_v2_test.go`; doubles as hand-readable documentation of the v2 shape. | ||
|
|
||
| ## Files | ||
|
|
||
| | File | Description | | ||
| | --- | --- | | ||
| | `v2-minimal.json` | Smallest meaningful schema-v2 file: `schema` + a primary backend. | | ||
| | `v2-with-gmeta-mirror.json` | Primary v2 + a gmeta mirror (write-only fan-out). | | ||
| | `v2-with-git-config.json` | Primary v2 + a git destination override (separate checkpoint repo). | | ||
| | `v2-kitchen-sink.json` | Every documented field populated — useful as a reference. | | ||
| | `legacy-equivalent.json` | Legacy-shape settings that synthesizes to the same struct as `v2-kitchen-sink.json`. | | ||
|
|
||
| ## Migration mapping | ||
|
|
||
| `legacy-equivalent.json` and `v2-kitchen-sink.json` are paired: loading either | ||
| through `LoadV2FromBytes` produces an identical `*Settings` value (modulo | ||
| defaults that round-trip correctly). The pairing is the most concrete way | ||
| to read the legacy → v2 mapping. | ||
|
|
||
| When adding a new legacy field that the synthesizer should translate, add it | ||
| to both files and the equivalence test confirms the round-trip. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| { | ||
| "enabled": true, | ||
| "log_level": "debug", | ||
| "commit_linking": "always", | ||
| "external_agents": true, | ||
| "telemetry": true, | ||
| "sign_checkpoint_commits": true, | ||
| "summary_timeout_seconds": 60, | ||
| "summary_generation": { | ||
| "provider": "claude-code", | ||
| "model": "sonnet" | ||
| }, | ||
| "redaction": { | ||
| "pii": { | ||
| "enabled": true, | ||
| "email": true, | ||
| "phone": true, | ||
| "address": false | ||
| } | ||
| }, | ||
| "strategy_options": { | ||
| "checkpoints_version": 2, | ||
| "gmeta": true, | ||
| "checkpoint_remote": { | ||
| "provider": "github", | ||
| "repo": "myorg/myrepo-checkpoints" | ||
| }, | ||
| "summarize": {"enabled": true}, | ||
| "filtered_fetches": true, | ||
| "push_sessions": true, | ||
| "full_transcript_generation_retention_days": 90 | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| { | ||
| "schema": 2, | ||
| "enabled": true, | ||
| "logging": { | ||
| "level": "debug" | ||
| }, | ||
| "checkpoints": { | ||
| "primary": {"type": "v2"}, | ||
| "mirrors": [ | ||
| {"type": "gmeta"} | ||
| ], | ||
| "git": { | ||
| "provider": "github", | ||
| "repo": "myorg/myrepo-checkpoints" | ||
| }, | ||
| "full_transcript_retention_days": 90, | ||
| "sign_commits": true, | ||
| "filtered_fetches": true, | ||
| "push_sessions": true | ||
| }, | ||
| "hooks": { | ||
| "commit_linking": "always" | ||
| }, | ||
| "features": { | ||
| "summarize": true, | ||
| "external_agents": true | ||
| }, | ||
| "redaction": { | ||
| "pii": { | ||
| "enabled": true, | ||
| "email": true, | ||
| "phone": true, | ||
| "address": false | ||
| } | ||
| }, | ||
| "telemetry": true, | ||
| "summary_generation": { | ||
| "provider": "claude-code", | ||
| "model": "sonnet", | ||
| "timeout_seconds": 60 | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "schema": 2, | ||
| "checkpoints": { | ||
| "primary": {"type": "v2"} | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.