* chore: add prettier config and CI auto-formatter
Adds .prettierrc and .prettierignore (config only, no local enforcement).
Formatting runs in CI via the prettier.yml workflow: on every push to a
non-main branch, Prettier rewrites changed files and commits the result back
using GITHUB_TOKEN. Developers never need to run Prettier locally.
Intentionally excludes husky/lint-staged — local pre-commit hooks are the
wrong layer for a formatter that the whole team doesn't need installed.
Also adds .envrc.local to .gitignore for personal local shell overrides.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 27336650d2ee
* chore: format with prettier [skip ci]
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* feat(config): persist per-project agent config and resolve it at spawn
Each project can now carry its own agent config (model, permissions,
adapter-specific keys) that survives daemon restart and is resolved into
the launch command when a session spawns.
- storage: add nullable projects.agent_config JSON column (migration 0008);
marshal/unmarshal in the store so the domain carries map[string]any
- resolution: session manager loads the project row and populates
LaunchConfig.Config before GetLaunchCommand
- validation: claude-code declares a ConfigSpec (model, permissions) and
rejects unknown keys / bad types / bad enums at spawn; it applies the
model override and config-driven permission mode (explicit Permissions
still wins)
- surface: PUT /projects/{id}/agent-config + `ao project set-config`
(--set/--config-json/--clear), config shown in `ao project get`
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(claudecode): validate string-list/required config keys and unhandled types
Address review on per-project agent config validation:
- handle ConfigFieldStringList (list of strings) explicitly
- reject unhandled ConfigFieldType via a default case rather than
silently passing
- enforce Required fields are present
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(config): make per-project agent config a typed struct
Replace the free-form map[string]any agent config with a typed
domain.AgentConfig{Model, Permissions} so values are validated when set
(CLI/API) instead of silently dropped at spawn, and the OpenAPI/TS schema
and UI get real typed fields.
- domain: AgentConfig struct + Validate(); PermissionMode moves to domain
and ports re-exports it as a type alias (zero adapter churn)
- storage: marshal/unmarshal the typed struct (IsZero → SQL NULL)
- service: validate on Add and SetAgentConfig; read-model exposes a typed
*AgentConfig
- claudecode: read typed cfg.Config.Model/.Permissions; drop the
map/spec-based validateConfig in favor of the typed Validate()
- cli: typed `ao project set-config --model/--permission/--clear`
- docs: add docs/design/per-project-config.md blueprint sequencing the
remaining # Projects fields toward fully typed per-project config
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(config): full typed per-project ProjectConfig (store, resolve, surface)
Expand per-project config from agentConfig-only to the full legacy
`projects.<id>` surface, modeled as one typed domain.ProjectConfig
persisted in a single projects.config JSON column.
Wired end-to-end at spawn:
- defaultBranch → base branch for the session worktree (ports.WorkspaceConfig.BaseBranch)
- env → merged into the runtime env (AO-internal vars still win)
- symlinks → repo files linked into the workspace
- postCreate → commands run in the workspace (OS-agnostic shell)
- agentRules / agentRulesFile / orchestratorRules → merged into the prompt
- worker/orchestrator role overrides → harness + agent-config resolution
Stored + validated + surfaced now, consumption deferred (no consumer yet):
tracker, scm(+webhook), opencodeIssueSessionStrategy; sessionPrefix feeds
the display prefix only (session-id generation unchanged).
Validation lives on domain.ProjectConfig.Validate() and runs when config is
set (CLI/API). PermissionMode/AgentConfig stay typed; harness names validated
via domain.AgentHarness.IsKnown().
Surface: PUT /projects/{id}/config (replaces /agent-config) + typed
`ao project set-config` flags (--default-branch/--env/--symlink/--post-create/
--agent-rules/--worker-agent/… or --config-json). OpenAPI + TS regenerated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(lint): tighten symlink dir perms to 0o750 (gosec G301)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(config): centralize default project config + tests
Add domain.DefaultProjectConfig / ProjectConfig.WithDefaults with a single
DefaultBranchName ("main") source of truth, replacing the literal "main"
scattered in the read-model and the gitworktree adapter. Unconfigured
projects now resolve the default branch through one path; every other field
defaults to its zero value.
Tests: defaults present for all fields (DefaultProjectConfig/WithDefaults),
and an unconfigured project reports the default branch + derived session
prefix while omitting the empty config object.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(config): encode documented defaults (branch=main, tracker=github)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(config): fail-safe paths for missing/corrupt per-project config
Address review on default-config / fail-safe spawning:
- projectRules: a missing AgentRulesFile is optional context, skipped
rather than aborting every spawn (only a real read error surfaces)
- store: a corrupt config JSON column degrades to a zero config instead
of failing GetProject/ListProjects/FindProjectByPath for that row
- restore: re-apply the project's resolved AgentConfig so a configured
model/permissions carry across a restore (matches fresh spawn)
Tests: missing rules file skips, corrupt config degrades to zero, restore
applies the project agent config.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(config): trim per-project config to consumer-backed fields
Drop config that has no live consumer yet, so this PR lands only the
fields actually read at spawn/display:
- Remove prompt rules (agentRules, agentRulesFile, orchestratorRules)
from ProjectConfig. Project/agent instructions belong on the system
prompt path or repo-local AGENTS.md, not another rules family.
- Remove future-only integration config with no consumer: tracker, scm,
scm.webhook, and opencodeIssueSessionStrategy (plus their types,
constants, the github tracker default, CLI flags, and spec schemas).
These return in focused PRs alongside the code that reads them.
Kept: defaultBranch, sessionPrefix, env, symlinks, postCreate,
agentConfig (model/permissions), and worker/orchestrator role
overrides. Cross-agent model/permissions support stays follow-up (#157).
Regenerated openapi.yaml + frontend schema.ts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(config): reject unknown config JSON keys; confine symlink paths
Two review hardenings on the now-trimmed per-project config surface:
- Project add/set-config endpoints decode with DisallowUnknownFields, so
a misspelled or removed config field surfaces as a clear 400 instead
of being silently dropped. Locks the removals from e213b68 (and any
future trims) at the API gate. Covered by new controllers test.
- applySymlinks now refuses absolute paths and any ".." segment via a
safeRelPath guard, so a project config cannot escape the project or
workspace tree via a malicious symlinks entry. Covered by new
session_manager test.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(config): reject symlink path traversal at config write time
greptile flagged ProjectConfig.Symlinks as a write-time path-traversal
gap on PR #154 — the runtime guard in applySymlinks catches a malicious
entry on every spawn, but the config itself accepted it. Move the check
into ProjectConfig.Validate so a bad symlinks entry surfaces as
INVALID_PROJECT_CONFIG when set (CLI/API) instead of silently sitting in
the row until the next spawn. The runtime guard stays as
defense-in-depth.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: harshitsinghbhandari <24b4506@iitb.ac.in>