Summary
docs/design/per-project-config.md has drifted from the implementation. The doc's status header acknowledges the current shape ("one projects.config JSON column", PUT /projects/{id}/config, ao project set-config), but the rest of the doc still describes the originally proposed per-field storage and per-group API surfaces. Anyone reading the doc fresh will get a contradictory picture.
Concrete deltas (non-exhaustive — please fully audit when picking this up)
-
Storage strategy section vs. reality — Doc proposes:
- Scalar fields (
default_branch, session_prefix, …) → typed columns on projects
- Small structured blobs (
agent_config, tracker, scm, symlinks, post_create) → nullable JSON columns
env → child table project_env (key/value rows)
Reality: the whole ProjectConfig is persisted as one JSON blob (projects.config) per the status header and backend/internal/domain/projectconfig.go comment: "persisted as one JSON blob per project."
-
API surface section vs. reality — Doc proposes focused routes (PUT /projects/{id}/agent-config, PUT /projects/{id}/env, etc.) "rather than one mega-PUT." Reality (per status header) is exactly one PUT /projects/{id}/config. The status header and the surface section contradict each other in the same document.
-
CLI surface — Doc lists examples like ao project set-config --model --permission, ao project env set KEY=VAL. Verify these match the actual ao project subcommands and flags shipped today.
-
Field catalog table — The "Storage today" / "Target" columns describe per-column homes and the project_env child table that aren't the live model. Either retarget the table to "field → key inside the projects.config JSON blob," or flag clearly that the per-column plan was superseded.
-
Sequencing section — Slices 2 ("identity scalars") and 3 ("workspace provisioning: env, symlinks, postCreate") were originally framed as separate column/table migrations. They effectively landed together inside the single config blob; the sequence story needs to be either rewritten to match what shipped or annotated to show what slices remain.
-
Typed model code block — Matches backend/internal/domain/projectconfig.go today (DefaultBranch, SessionPrefix, AgentConfig, Worker, Orchestrator, Env, Symlinks, PostCreate). This part is fine — preserve it.
Suggested approach
- Treat the status header as the new ground truth (single
projects.config JSON blob, single PUT /projects/{id}/config, ao project set-config).
- Rewrite "Storage strategy", "Surface (per field)", and "Sequencing" so the recommended pattern matches what shipped, and any remaining per-field column work is clearly marked as future-only.
- Keep the "Typed model" section, the "Field catalog" left columns (YAML field / type), and the principle ("typed over map") — these are still accurate.
- Audit the doc end-to-end against
backend/internal/domain/projectconfig.go, the projects table schema, and the actual ao project CLI/HTTP surface before publishing.
References
Summary
docs/design/per-project-config.mdhas drifted from the implementation. The doc's status header acknowledges the current shape ("oneprojects.configJSON column",PUT /projects/{id}/config,ao project set-config), but the rest of the doc still describes the originally proposed per-field storage and per-group API surfaces. Anyone reading the doc fresh will get a contradictory picture.Concrete deltas (non-exhaustive — please fully audit when picking this up)
Storage strategy section vs. reality — Doc proposes:
default_branch,session_prefix, …) → typed columns onprojectsagent_config,tracker,scm,symlinks,post_create) → nullable JSON columnsenv→ child tableproject_env(key/value rows)Reality: the whole
ProjectConfigis persisted as one JSON blob (projects.config) per the status header andbackend/internal/domain/projectconfig.gocomment: "persisted as one JSON blob per project."API surface section vs. reality — Doc proposes focused routes (
PUT /projects/{id}/agent-config,PUT /projects/{id}/env, etc.) "rather than one mega-PUT." Reality (per status header) is exactly onePUT /projects/{id}/config. The status header and the surface section contradict each other in the same document.CLI surface — Doc lists examples like
ao project set-config --model --permission,ao project env set KEY=VAL. Verify these match the actualao projectsubcommands and flags shipped today.Field catalog table — The "Storage today" / "Target" columns describe per-column homes and the
project_envchild table that aren't the live model. Either retarget the table to "field → key inside theprojects.configJSON blob," or flag clearly that the per-column plan was superseded.Sequencing section — Slices 2 ("identity scalars") and 3 ("workspace provisioning: env, symlinks, postCreate") were originally framed as separate column/table migrations. They effectively landed together inside the single config blob; the sequence story needs to be either rewritten to match what shipped or annotated to show what slices remain.
Typed model code block — Matches
backend/internal/domain/projectconfig.gotoday (DefaultBranch,SessionPrefix,AgentConfig,Worker,Orchestrator,Env,Symlinks,PostCreate). This part is fine — preserve it.Suggested approach
projects.configJSON blob, singlePUT /projects/{id}/config,ao project set-config).backend/internal/domain/projectconfig.go, theprojectstable schema, and the actualao projectCLI/HTTP surface before publishing.References
backend/internal/domain/projectconfig.go