Skip to content

AI support copilot Phase 2: gated artisan changes over SSH#3578

Open
bernardhanna wants to merge 3 commits into
ai-support-copilot-phase1from
ai-support-copilot-phase2
Open

AI support copilot Phase 2: gated artisan changes over SSH#3578
bernardhanna wants to merge 3 commits into
ai-support-copilot-phase1from
ai-support-copilot-phase2

Conversation

@bernardhanna

Copy link
Copy Markdown
Collaborator

Summary

Stacked on top of #3577 (Phase 1). Adds Phase 2: the support copilot can run server maintenance commands through the same dry-run → APPROVE → execute → report pipeline as every other write action. Disabled by default (SUPPORT_AI_ARTISAN_ENABLED=false).

  • Allowlist-first (ArtisanActionRegistry): the AI may only pick a permitted command; every arg/option is type-validated (email/token/name). Seeded with support:user-audit, support:event-audit, support:user-restore, support:user-update-profile.
  • Guarded raw fallback (SUPPORT_AI_ARTISAN_ALLOW_RAW=true): AI-proposed commands are rejected on shell metacharacters and a destructive deny-list (migrate:fresh, db:wipe, tinker, down, …), treated as writes, and APPROVE-gated.
  • Execution safety (ArtisanCommandRunner): runs via the Process array form (php artisan …) so values can't be shell-interpreted; write commands preview with --dry-run, read-only run as-is, raw is never auto-simulated. Re-validated at execution time rather than trusting the stored approval payload; output captured + truncated.
  • Triage gains the artisan_command case type (only offered when enabled) + artisan_command_name/artisan_args/artisan_raw_command.
  • Wired into diagnostics, the approval email (exact command + preview), execute job, completion email; support:ai:setup-check reports artisan status; allowed_write_actions includes artisan_command.

Review note

Base is the Phase 1 branch so the diff is only Phase 2. Merge after #3577. No migration needed — Phase 2 uses the existing action log.

Test plan

  • vendor/bin/phpunit tests/Unit/Support/ArtisanCommandRunnerTest.php (11 tests) green
  • With SUPPORT_AI_ARTISAN_ENABLED=true: a maintenance ticket triages as artisan_command, dry-run email shows the exact php artisan … command (+ --dry-run preview for write commands)
  • APPROVE runs the command; completion email shows command + output
  • Deny-listed / shell-metacharacter raw commands are rejected
  • With the flag off, artisan_command is never offered and the runner fails safe

Made with Cursor

bernardhanna and others added 2 commits June 23, 2026 10:32
Lets the support copilot run server maintenance commands through the same
dry-run -> APPROVE -> execute -> report pipeline as other write actions.
Disabled by default (SUPPORT_AI_ARTISAN_ENABLED=false).

- ArtisanActionRegistry: allowlist of permitted commands with per-argument
  type validation (email/token/name). Seeded with support:user-audit,
  support:event-audit, support:user-restore, support:user-update-profile.
- ArtisanCommandRunner: builds/validates a plan, dry-runs (write commands get
  --dry-run; read-only run as-is; raw is never auto-simulated), and executes via
  the Process array form so values can't be shell-interpreted. Re-validates at
  execution time rather than trusting the stored approval payload.
- Guarded raw fallback (SUPPORT_AI_ARTISAN_ALLOW_RAW): AI-proposed artisan
  commands are rejected on shell metacharacters and a destructive deny-list
  (migrate:fresh, db:wipe, tinker, down, ...), treated as writes, APPROVE-gated.
- Triage gains the artisan_command case type (only offered when enabled) plus
  artisan_command_name/artisan_args/artisan_raw_command fields.
- Wired into diagnostics (dry-run preview), the approval email (exact command +
  preview), execute job, and completion email (command + output). setup-check
  now reports artisan status; allowed_write_actions includes artisan_command.
- Tests for registry validation and the raw-command guard; docs updated.

Co-authored-by: Cursor <cursoragent@cursor.com>
Lets the support copilot make editorial text changes to allowlisted content
records through the same dry-run -> APPROVE -> execute -> report pipeline.
Records are Nova resources, so a human can also review/tweak them there.
Disabled by default (SUPPORT_AI_CONTENT_ENABLED=false).

- ContentActionRegistry: allowlist of editable content models (pages, slides,
  FAQ items, menus, events, podcasts, partners, ...). Page singletons resolve
  automatically; other records by id or a unique lookup field.
- ContentFieldResolver: editable columns resolved at runtime to string/text
  columns minus a structural deny-list (URLs, slugs, flags, relations,
  identifiers, SEO/keyword/category) and minus non-string casts. No per-model
  column list to maintain.
- ContentUpdateService: text-only value guards (rejects URLs, www refs, HTML/
  markup, over-length), protects Laravel translation-key fields, computes an
  exact before/after diff for the approval email, and re-validates at execution
  time rather than trusting the stored payload.
- Triage gains the content_update case type (only offered when enabled) plus
  content_model/content_identifier/content_changes/content_summary.
- Wired into diagnostics (before/after preview), approval email (diff),
  execute job, and completion email; setup-check reports content status;
  allowed_write_actions includes content_update.
- Unit tests for value guards, registry, and pre-DB plan validation; docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
AI support copilot Phase 3: content edits on Nova-managed records
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant