Skip to content

PRD: Lightweight Railway-like AP Environment editor #100

@aimeritething

Description

@aimeritething

Problem Statement

The AP Environment section currently mixes structured rows, editor-only reference tokens, hidden per-row Reference context, helper variables, and Kubernetes valueFrom presentation in a way that is hard for users to reason about. Users want a Railway-like AP environment experience where raw .env source is the canonical editing model, the default UI remains structured and easy to scan, DB references can be inserted without learning all underlying Kubernetes details, and resolved values can be revealed or copied on demand without storing DB passwords as plaintext AP environment values.

Solution

Build a lightweight Railway-like AP Environment editor. AP Environment Raw Source becomes the canonical product state in .env form. The Environment section defaults to an AP Environment Structured View over that Raw Source, where clean saved values are masked, dirty/editing rows show raw values, and Reference insertion is available from both the structured row editor and the Raw editor. On Update, Brain compiles Raw Source into runtime AP environment entries. AP Environment References such as ${{postgres.DATABASE_URL}} resolve into ordinary runtime env entries, using generated supporting variables backed by DB credential Secret refs for complete DSNs instead of copying DB passwords into AP plaintext env values.

User Stories

  1. As an AP operator, I want the Environment section to default to a structured list, so that I can scan my AP environment without reading raw text first.
  2. As an AP operator, I want Raw editor to be the canonical editing model, so that I can paste and maintain .env content directly.
  3. As an AP operator, I want to switch between structured view and Raw editor without losing comments, blank lines, ordering, or formatting, so that my environment source remains understandable.
  4. As an AP operator, I want structured rows to follow Raw Source order, so that the UI matches the organization I wrote in Raw editor.
  5. As an AP operator, I want comments and blank lines preserved in Raw Source, so that I can document environment sections.
  6. As an AP operator, I want inline comments and nearby comment blocks preserved when editing structured rows, so that small UI edits do not destroy my Raw Source context.
  7. As an AP operator, I want duplicate env keys blocked, so that there is no ambiguity about which value applies at runtime.
  8. As an AP operator, I want clean saved environment values masked in the structured list, so that secrets and non-secrets are treated consistently.
  9. As an AP operator, I want all structured preview values to display as *******, so that the product does not guess which variables are sensitive.
  10. As an AP operator, I want clean saved rows to reveal values only through explicit per-row actions, so that the default surface is safe to open.
  11. As an AP operator, I want copy on a clean saved structured row to copy the fully resolved value, so that I can use it for debugging or external tooling.
  12. As an AP operator, I want reveal on a clean saved structured row to show the fully resolved value, so that I can inspect what the application receives.
  13. As an AP operator, I want reveal to be per-row rather than reveal-all, so that one action does not expose every AP environment value.
  14. As an AP operator, I want reveal to automatically re-mask after a short interval, so that exposed values do not remain on screen.
  15. As an AP operator, I want reveal state cleared when switching resources, closing the panel, or entering Raw editor, so that stale secrets do not leak across contexts.
  16. As an AP operator, I want reveal and copy to require no extra confirmation, so that common workflows stay fast.
  17. As an AP operator, I want reveal and copy responses not cached, so that resolved values are not persisted in app state.
  18. As an AP operator, I want editing rows to display raw values directly, so that I can see what I am changing.
  19. As an AP operator, I want newly added rows to display raw values directly, so that I do not need reveal for values I just typed.
  20. As an AP operator, I want dirty rows to display raw values even when collapsed, so that I can review pending changes before Update.
  21. As an AP operator, I want dirty rows with hand-written secret-looking values to display raw values before Update, so that draft review is honest and editable.
  22. As an AP operator, I want saved rows to return to masked preview after Update succeeds, so that the default structured list stays safe.
  23. As an AP operator, I want row edit mode to show raw source rather than resolved values, so that editing always changes configuration source.
  24. As an AP operator, I want the Raw editor to show complete Raw Source without masking, so that Raw editor behaves as an actual source editor.
  25. As an AP operator, I want Raw editor syntax errors to keep me in Raw editor, so that invalid source is not misrepresented in structured view.
  26. As an AP operator, I want structured row editing to allow temporary syntax errors while typing, so that the editor does not fight normal input.
  27. As an AP operator, I want Update disabled while Raw Source has syntax or unresolved Reference errors, so that broken environment configuration is not saved.
  28. As an AP operator, I want Discard to abandon all pending environment draft changes, so that I can return to the saved Raw Source.
  29. As an AP operator, I want Environment changes to remain local until I click Update, so that accidental edits do not mutate the AP.
  30. As an AP operator, I want the env-focused leave guard to mention Environment Variable changes, so that the warning matches the surface I am viewing.
  31. As an AP operator, I want the Update button label to remain Update, so that AP settings footers stay consistent.
  32. As an AP operator, I want to type ${{ in Raw editor to open an Insert Reference menu, so that I can insert DB-backed values while writing .env.
  33. As an AP operator, I want to type ${{ in a structured row value editor to open the same Insert Reference menu, so that structured editing and raw editing share one Reference flow.
  34. As an AP operator, I want an Insert Reference button in structured rows, so that I do not need to remember the ${{ trigger.
  35. As an AP operator, I want selecting a Reference menu item to complete the current ${{... token, so that partially typed references are replaced cleanly.
  36. As an AP operator, I want selecting a Reference menu item from the button to insert a complete ${{db.VAR}} expression, so that button-driven insertion works without manual syntax.
  37. As an AP operator, I want the Insert Reference menu to show variable names, types, and availability, so that I can choose the right DB-provided value.
  38. As an AP operator, I do not want the Insert Reference menu to reveal resolved values, so that insertion does not become an accidental secret viewing surface.
  39. As an AP operator, I want inserted References to use canonical syntax, so that Raw Source remains tidy.
  40. As an AP operator, I want DB resource names in References to match actual DB resource identity, so that References do not resolve ambiguously.
  41. As an AP operator, I want Reference variable names to be forgiving about case while normalizing to canonical names, so that hand-written references are easy to fix.
  42. As an AP operator, I want first-version Reference syntax to support Kubernetes-safe DB names, so that syntax remains simple.
  43. As an AP operator, I want ${{postgres.DATABASE_URL}} to mean a complete connection DSN, so that the variable is directly usable by common application frameworks.
  44. As an AP operator, I want DATABASE_URL References not to automatically append a default database path, so that the product does not silently choose a database name.
  45. As an AP operator, I want References usable as part of a larger value, so that I can append database paths or query strings myself.
  46. As an AP operator, I want DB Reference insertion from a canvas AP-DB connection to default to one private DATABASE_URL Reference, so that connecting an AP to a DB is fast and familiar.
  47. As an AP operator, I want Insert Reference to avoid overwriting existing env keys, so that existing configuration is not destroyed.
  48. As an AP operator, I want conflicting inserted names to use DB identity fallback names, so that generated names remain understandable.
  49. As an AP operator, I want DATABASE_URL compilation to use generated supporting variables backed by DB Secret refs, so that DB passwords are not stored as AP plaintext values.
  50. As an AP operator, I want supporting variables to be generated from DB identity and field names, so that compiled env is stable and explainable.
  51. As an AP operator, I want supporting variable names to avoid collisions with explicit Raw Source keys, so that user-authored env always wins.
  52. As an AP operator, I want supporting variables reused across rows for the same DB field, so that compiled env does not grow unnecessarily.
  53. As an AP operator, I want explicit Raw Source rows not reused as compiler supporting variables, so that the compiler does not couple generated DSNs to user-authored names.
  54. As an AP operator, I want supporting variables not written back into Raw Source, so that Raw editor remains focused on user-authored configuration.
  55. As an AP operator, I want supporting variables not shown as primary structured rows, so that the list reflects my Raw Source.
  56. As an AP operator, I want supporting variables visible in row details for compiled References, so that I can understand what runtime env will be generated.
  57. As an AP operator, I want broken References to remain visible in structured view, so that deleted or inaccessible DBs do not silently remove configuration.
  58. As an AP operator, I want broken References to block Update until fixed or explicitly converted to plain text, so that runtime does not receive accidental literal Reference syntax.
  59. As an AP operator, I want unresolved Reference variables to block Update, so that typos do not become runtime strings.
  60. As an AP operator, I want runtime compilation failures to block Update, so that the AP is not saved with unusable environment configuration.
  61. As an AP operator, I want reveal/copy permission failures not to block Update when runtime compilation can still succeed, so that viewing privileges and deployment privileges are separated.
  62. As an AP operator, I want reveal/copy for saved rows to resolve the saved Raw Source, so that I inspect what the current AP configuration means.
  63. As an AP operator, I want reveal/copy disabled for dirty rows, so that the UI does not confuse draft values with saved runtime values.
  64. As an AP operator, I want raw editor copy behavior to copy Raw Source, so that source editing workflows remain predictable.
  65. As an AP operator, I want structured row copy behavior to copy resolved values, so that runtime inspection workflows remain useful.
  66. As a platform engineer, I want AP-DB canvas edges to derive from Raw Source References and compiled env evidence, so that canvas relationships reflect user intent and runtime facts.
  67. As a platform engineer, I want AP Environment References resolved at AP settings Update time, so that v1 does not require a continuous variables reconciler.
  68. As a platform engineer, I want DB Secret content rotation to work when Secret name/key stay stable, so that generated valueFrom entries benefit from Kubernetes Secret updates.
  69. As a platform engineer, I want DB host/template changes not to auto-rewrite AP specs in v1, so that the lightweight model stays bounded.
  70. As an implementing agent, I want the Raw Source parser and compiler isolated behind small interfaces, so that the core behavior can be tested without rendering React.

Implementation Decisions

  • AP Environment Raw Source is the canonical product state for user-authored AP environment configuration. It is .env text, not a Kubernetes env array and not a hidden per-row data model.
  • The structured Environment list is a view over Raw Source. It must never become a second source of truth.
  • AP settings Update saves Raw Source and compiled runtime env together. The compiled env is generated output for the AP workload.
  • Raw Source supports direct values, comments, blank lines, runtime env expansion with $(NAME), and AP Environment References with ${{db.VAR}}.
  • Raw Source does not support arbitrary legacy Kubernetes valueFrom authoring in v1. The product is not launched, so legacy AP environment compatibility is explicitly not required.
  • The AP product model needs a field for Raw Source. Use a product field rather than treating Raw Source as incidental UI metadata. The compiled runtime env remains available to the AP renderer as generated environment entries.
  • The Raw Source parser should preserve ordering, blank lines, full-line comments, inline comments, and original value quoting/escaping where possible.
  • Structured row edits mutate the single Raw Source draft. They should preserve surrounding trivia and formatting rather than reconstructing the entire file.
  • Raw editor edits mutate the same Raw Source draft. Switching back to structured view reparses the draft.
  • Raw Source syntax errors prevent leaving Raw editor for structured view.
  • Duplicate env keys are invalid and block Update.
  • Clean saved rows in structured view mask all values as *******, without classifying secret versus plain variables.
  • Editing, new, or dirty rows display raw values. Once Update succeeds and the row is clean, it returns to masked preview.
  • Raw editor displays complete Raw Source and does not mask values. This is an intentional source-editing surface.
  • Per-row reveal and copy exist only for clean saved structured rows. Dirty rows use raw display and do not offer resolved reveal/copy.
  • Structured reveal and copy return fully resolved values. Raw editor copy returns Raw Source.
  • Resolved values are treated as sensitive regardless of variable name. Do not split reveal/copy behavior into secret and non-secret paths in v1.
  • Resolved value responses must not be cached by the frontend or backend HTTP layer. Values should exist only transiently in component memory for reveal or the clipboard action.
  • Reveal is per-row and auto-remasks after 30 seconds. Reveal state is cleared when closing the panel, switching resources, or entering Raw editor.
  • Reveal/copy do not require a second confirmation.
  • Insert Reference menu opens when typing ${{ in Raw editor or in a structured row value editor. A structured row also has an Insert Reference button.
  • Insert Reference menu shows DB variable name, type, and availability only. It does not show resolved values.
  • Selecting a Reference menu item replaces the current incomplete ${{... token when one exists; otherwise it inserts a complete expression.
  • Inserted References use canonical syntax. DB resource identity is exact; variable names can be resolved case-insensitively and normalized to canonical casing.
  • First-version Reference syntax supports Kubernetes-safe DB resource names. Cross-namespace syntax is deferred and may use namespace/name.VAR later.
  • Reference variable options come from DB Service status variables plus product aliases for common DSN values.
  • DATABASE_URL is a complete connection DSN, including credentials when the engine requires credentials.
  • DATABASE_URL does not automatically include a default database path suffix. Users can append a path or query string by composing the value around the Reference.
  • ${{postgres.DATABASE_URL}} and other References may appear inside larger values.
  • The AP-DB canvas connection insert flow defaults to one private DSN Reference, normally DATABASE_URL=${{postgres.DATABASE_URL}}.
  • Insertion must not overwrite existing keys. Prefer DATABASE_URL; on conflict use a DB-identity fallback such as {DB_IDENTITY}_DATABASE_URL, then stable numeric suffixes as needed.
  • Runtime compilation for DB DSNs uses generated supporting variables rather than copying DB password plaintext into the compiled AP env.
  • DATABASE_URL=${{postgres.DATABASE_URL}} compiles into generated supporting variables for user, password, host, and port, then a DATABASE_URL value composed with runtime $(SUPPORTING_NAME) expansion.
  • Supporting variables are named {DB_IDENTITY}_{FIELD} with collision handling. They never override explicit Raw Source env keys.
  • The same DB and same field should reuse the same generated supporting variable across compiled rows.
  • Explicit user-authored rows are not reused as compiler supporting variables, even if they point at the same DB Secret source.
  • Supporting variables are not written back into Raw Source and are not primary structured rows. They may be shown in details for a Reference row.
  • Broken References are preserved in Raw Source and structured view. They block Update until fixed or explicitly converted to plain text.
  • Runtime-required Reference resolution failures block Update. Reveal/copy permission failures do not block Update if runtime compilation can still generate valid env entries.
  • AP Environment References are resolved and compiled on AP settings Update. DB changes do not automatically rewrite AP specs in v1, though stable Secret refs allow Secret content rotation to naturally affect runtime.
  • Canvas AP-DB connection detection should prefer Raw Source Reference evidence when available and may use compiled env evidence as a fallback for generated runtime entries.
  • The env-focused leave guard should use Environment Variable wording. The visual Update button remains Update.
  • Create deep, testable modules for .env Raw Source parsing/preserving, AP Environment Reference parsing/resolution, runtime env compilation, structured-row projection/edit application, resolved-value API behavior, and canvas connection evidence extraction.

Testing Decisions

  • Tests should assert user-visible behavior and stable module contracts, not React implementation details or private helper names except where supporting variable naming is a documented compiler contract.
  • Add parser tests for comments, blank lines, inline comments, quote preservation, env ordering, duplicate-key errors, temporary syntax errors, and canonical Reference normalization.
  • Add compiler tests for direct values, composed values, complete DATABASE_URL References, no automatic database path suffix, suffix/path composition, supporting variable generation, collision handling, reuse across rows, and explicit-row precedence.
  • Add resolver tests for fully resolved values, missing dependency failures, broken DB references, unresolved variable references, no partial reveal, no caching headers, and per-row target resolution.
  • Add structured projection tests for clean saved rows masked, dirty/editing rows raw, Raw editor full source display, row order following Raw Source, supporting variables folded into row details, and Insert Reference menu metadata without resolved values.
  • Add AP settings draft tests showing all env mutations stay local until Update and Update saves Raw Source plus compiled runtime env.
  • Add AP-DB canvas connection tests showing Raw Source References create APToDB edges and compiled env fallback still works for generated runtime entries.
  • Reuse prior art from existing environment row tests, AP env patch tests, claim mapper env tests, settings draft backing tests, pending AP-DB connection tests, and canvas detected connection tests.
  • Prefer isolated tests for parser/compiler/resolver modules before component tests. Component tests should cover only the externally observable UI contract.

Out of Scope

  • A full Railway-style variables/secrets manager with independent variable ownership, deployment-time rendering history, organization/shared variables, sealed variables, or broad audit workflows.
  • Automatic AP spec rewrites when DB host templates or DB variable definitions change.
  • Legacy AP environment compatibility for old valueFrom rows or old UI-only token/helper behavior.
  • Arbitrary Kubernetes valueFrom editing in Raw editor.
  • Reveal all.
  • Secret/plain classification in v1; all resolved env values are treated as sensitive.
  • Cross-namespace Reference syntax beyond reserving an obvious future path.
  • Choosing database/schema/path inside ${{db.DATABASE_URL}}.
  • Default database path suffixes such as /postgres.
  • Using DB Secret keys that already contain complete DSNs as a separate compile path.
  • Reusing explicit user-authored rows as compiler supporting variables.

Further Notes

  • This PRD follows ADR-0018, which revises the earlier AP Environment token/helper model while preserving the Database Binding boundary from ADR-0002.
  • The implementation should avoid Crossplane-era compatibility assumptions.
  • Raw Source should be treated as product state, not an implementation annotation, because users author and expect it to survive round trips.
  • The current implementation already has many useful test seams around env rows, token handling, AP env patching, claim mapping, and detected AP-DB connections, but the old token/helper behavior should be replaced rather than preserved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified and ready for an agent to implement

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions