{
  "feature": "password-reset",
  "version": "1.0.0",
  "description": "Allow users to reset their password via email verification",
  "category": "auth",
  "tags": [
    "password",
    "reset",
    "recovery",
    "security",
    "email"
  ],
  "fields": [
    {
      "name": "email",
      "type": "email",
      "required": true,
      "label": "Email Address",
      "placeholder": "you@example.com",
      "form": "request",
      "validation": [
        {
          "type": "required",
          "message": "Email is required"
        },
        {
          "type": "email",
          "message": "Please enter a valid email address"
        },
        {
          "type": "maxLength",
          "value": 255,
          "message": "Email is too long"
        }
      ]
    },
    {
      "name": "token",
      "type": "token",
      "required": true,
      "form": "reset",
      "sensitive": true,
      "validation": [
        {
          "type": "required",
          "message": "Reset token is missing"
        }
      ]
    },
    {
      "name": "new_password",
      "type": "password",
      "required": true,
      "label": "New Password",
      "form": "reset",
      "sensitive": true,
      "validation": [
        {
          "type": "required",
          "message": "New password is required"
        },
        {
          "type": "minLength",
          "value": 8,
          "message": "Password must be at least 8 characters"
        },
        {
          "type": "maxLength",
          "value": 64,
          "message": "Password must be less than 64 characters"
        },
        {
          "type": "pattern",
          "value": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$",
          "message": "Password must contain uppercase, lowercase, and a number"
        }
      ]
    },
    {
      "name": "confirm_new_password",
      "type": "password",
      "required": true,
      "label": "Confirm New Password",
      "form": "reset",
      "sensitive": true,
      "validation": [
        {
          "type": "required",
          "message": "Please confirm your new password"
        },
        {
          "type": "match",
          "field": "new_password",
          "message": "Passwords do not match"
        }
      ]
    }
  ],
  "rules": {
    "security": {
      "token": {
        "type": "cryptographic_random",
        "length_bytes": 32,
        "hash_before_storage": true,
        "algorithm": "sha256"
      },
      "token_expiry": {
        "minutes": 60
      },
      "single_use": true,
      "invalidate_previous": true,
      "rate_limit": {
        "window_seconds": 3600,
        "max_requests": 3,
        "scope": "per_email"
      },
      "rate_limit_global": {
        "window_seconds": 3600,
        "max_requests": 20,
        "scope": "per_ip"
      },
      "password_history": {
        "check_previous": false,
        "count": 0
      },
      "invalidate_sessions_on_reset": true
    },
    "email": {
      "case_sensitive": false,
      "trim_whitespace": true,
      "enumeration_prevention": true
    },
    "password_comparison": {
      "constant_time": true
    }
  },
  "flows": {
    "request_happy_path": {
      "description": "User requests a password reset link",
      "steps": [
        {
          "id": "validate_email",
          "action": "validate_fields",
          "description": "Check email format"
        },
        {
          "id": "normalize",
          "action": "normalize_email",
          "description": "Lowercase and trim"
        },
        {
          "id": "lookup",
          "action": "lookup_user_by_email",
          "description": "Find user in database",
          "on_fail": "request_email_not_found"
        },
        {
          "id": "check_rate_limit",
          "action": "check_rate_limit",
          "description": "Verify not too many requests",
          "on_fail": "rate_limited"
        },
        {
          "id": "invalidate_old",
          "action": "invalidate_previous_tokens",
          "description": "Cancel any existing reset tokens"
        },
        {
          "id": "generate_token",
          "action": "generate_reset_token",
          "description": "Create crypto random token, store hash in DB"
        },
        {
          "id": "send_email",
          "action": "send_reset_email",
          "description": "Email the reset link with token"
        },
        {
          "id": "emit_requested",
          "action": "emit",
          "event": "password_reset.requested"
        },
        {
          "id": "show_confirmation",
          "action": "show_message",
          "message": "If an account with that email exists, we've sent a password reset link."
        }
      ]
    },
    "request_email_not_found": {
      "description": "Email not in system — but we don't reveal that",
      "trigger": "user not found",
      "steps": [
        {
          "action": "emit",
          "event": "password_reset.email_not_found"
        },
        {
          "action": "show_message",
          "message": "If an account with that email exists, we've sent a password reset link."
        }
      ]
    },
    "reset_happy_path": {
      "description": "User submits new password with valid token",
      "steps": [
        {
          "id": "validate_fields",
          "action": "validate_fields",
          "description": "Check password requirements and match"
        },
        {
          "id": "validate_token",
          "action": "validate_reset_token",
          "description": "Hash the URL token, look up in DB, check expiry",
          "on_fail": "token_invalid"
        },
        {
          "id": "check_password_history",
          "action": "check_password_history",
          "description": "Optional: ensure new password differs from recent ones",
          "on_fail": "password_reused"
        },
        {
          "id": "hash_password",
          "action": "hash_new_password",
          "description": "bcrypt.hash(new_password, salt_rounds)"
        },
        {
          "id": "update_password",
          "action": "update_user_password",
          "description": "Save new hash to database"
        },
        {
          "id": "invalidate_token",
          "action": "invalidate_reset_token",
          "description": "Mark token as used — single use"
        },
        {
          "id": "invalidate_sessions",
          "action": "invalidate_all_sessions",
          "description": "Log out all active sessions for this user"
        },
        {
          "id": "emit_success",
          "action": "emit",
          "event": "password_reset.success"
        },
        {
          "id": "send_confirmation",
          "action": "send_confirmation_email",
          "description": "Notify user their password was changed"
        },
        {
          "id": "redirect",
          "action": "redirect",
          "target": "login",
          "message": "Password reset successful. Please sign in with your new password."
        }
      ]
    },
    "token_invalid": {
      "description": "Token expired, already used, or tampered with",
      "trigger": "token not found OR expired OR already used",
      "steps": [
        {
          "action": "emit",
          "event": "password_reset.token_invalid"
        },
        {
          "action": "show_error",
          "error": "RESET_TOKEN_INVALID"
        },
        {
          "action": "show_link",
          "target": "password-reset",
          "label": "Request a new reset link"
        }
      ]
    },
    "token_expired": {
      "description": "Token is valid but past expiry",
      "trigger": "token.created_at + token_expiry < now",
      "steps": [
        {
          "action": "emit",
          "event": "password_reset.token_expired"
        },
        {
          "action": "show_error",
          "error": "RESET_TOKEN_EXPIRED"
        },
        {
          "action": "show_link",
          "target": "password-reset",
          "label": "Request a new reset link"
        }
      ]
    },
    "password_reused": {
      "description": "New password matches a recent password",
      "trigger": "new password hash matches one in password_history",
      "steps": [
        {
          "action": "show_error",
          "error": "RESET_PASSWORD_REUSED"
        }
      ]
    },
    "rate_limited": {
      "description": "Too many reset requests",
      "trigger": "request_count >= rate_limit.max_requests",
      "steps": [
        {
          "action": "show_message",
          "message": "If an account with that email exists, we've sent a password reset link."
        }
      ]
    }
  },
  "outcomes": {
    "request_rate_limited": {
      "priority": 1,
      "error": "RESET_RATE_LIMITED",
      "given": [
        {
          "any": [
            {
              "field": "email_request_count",
              "source": "computed",
              "operator": "gt",
              "value": 3,
              "description": "More than 3 reset requests per hour for this email"
            },
            {
              "field": "ip_request_count",
              "source": "computed",
              "operator": "gt",
              "value": 20,
              "description": "More than 20 reset requests per hour from this IP"
            }
          ]
        }
      ],
      "result": "show \"If an account with that email exists, we've sent a password reset link.\" (same as success — don't reveal rate limiting)"
    },
    "reset_requested_unknown_email": {
      "priority": 2,
      "given": [
        {
          "field": "user",
          "source": "db",
          "operator": "not_exists",
          "description": "Email not found in database"
        }
      ],
      "then": [
        {
          "action": "emit_event",
          "event": "password_reset.email_not_found",
          "payload": [
            "email",
            "timestamp",
            "ip_address"
          ]
        }
      ],
      "result": "show SAME message as successful request (enumeration prevention)"
    },
    "reset_requested": {
      "priority": 3,
      "transaction": true,
      "given": [
        {
          "field": "email",
          "source": "input",
          "operator": "matches",
          "value": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
          "description": "Email is valid format"
        },
        {
          "field": "user",
          "source": "db",
          "operator": "exists",
          "description": "User exists in database"
        }
      ],
      "then": [
        {
          "action": "invalidate",
          "target": "reset_tokens",
          "scope": "all_for_user",
          "description": "Invalidate all previous reset tokens"
        },
        {
          "action": "create_record",
          "type": "reset_token",
          "target": "reset_token",
          "description": "Generate crypto.randomBytes(32), store SHA-256 hash, expires in 60 min"
        },
        {
          "action": "notify",
          "channel": "email",
          "template": "password_reset_email",
          "to": "user",
          "description": "Send reset link with raw token"
        },
        {
          "action": "emit_event",
          "event": "password_reset.requested",
          "payload": [
            "user_id",
            "email",
            "timestamp",
            "ip_address",
            "token_expires_at"
          ]
        }
      ],
      "result": "show \"If an account with that email exists, we've sent a password reset link.\""
    },
    "token_invalid": {
      "priority": 4,
      "error": "RESET_TOKEN_INVALID",
      "given": [
        {
          "any": [
            {
              "field": "token_hash",
              "source": "db",
              "operator": "not_exists",
              "description": "Token hash not found in database"
            },
            {
              "field": "token_used",
              "source": "db",
              "operator": "eq",
              "value": true,
              "description": "Token has already been used"
            }
          ]
        }
      ],
      "then": [
        {
          "action": "emit_event",
          "event": "password_reset.token_invalid",
          "payload": [
            "token_hash",
            "timestamp",
            "ip_address"
          ]
        }
      ],
      "result": "show \"This reset link is invalid. Please request a new one.\" with link to /forgot-password"
    },
    "token_expired": {
      "priority": 5,
      "error": "RESET_TOKEN_EXPIRED",
      "given": [
        {
          "field": "token_hash",
          "source": "db",
          "operator": "exists"
        },
        {
          "field": "token_created_at",
          "source": "db",
          "operator": "lt",
          "value": "now - 60 minutes",
          "description": "Token has expired (older than 60 minutes)"
        }
      ],
      "then": [
        {
          "action": "emit_event",
          "event": "password_reset.token_expired",
          "payload": [
            "user_id",
            "timestamp",
            "ip_address"
          ]
        }
      ],
      "result": "show \"This reset link has expired. Please request a new one.\" with link to /forgot-password"
    },
    "password_reused": {
      "priority": 6,
      "error": "RESET_PASSWORD_REUSED",
      "given": [
        {
          "field": "new_password",
          "source": "computed",
          "operator": "in",
          "value": "recent_password_hashes",
          "description": "New password matches one of the last N passwords"
        }
      ],
      "result": "show \"Please choose a password you haven't used recently\""
    },
    "password_reset_success": {
      "priority": 10,
      "transaction": true,
      "given": [
        {
          "field": "token",
          "source": "input",
          "operator": "exists",
          "description": "Token is present in URL"
        },
        {
          "field": "token_hash",
          "source": "db",
          "operator": "exists",
          "description": "SHA-256 hash of token matches a DB record"
        },
        {
          "field": "token_created_at",
          "source": "db",
          "operator": "gte",
          "value": "now - 60 minutes",
          "description": "Token has not expired"
        },
        {
          "field": "token_used",
          "source": "db",
          "operator": "eq",
          "value": false,
          "description": "Token has not been used (single-use)"
        },
        {
          "field": "new_password",
          "source": "input",
          "operator": "matches",
          "value": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,64}$",
          "description": "Password meets requirements"
        },
        {
          "field": "confirm_new_password",
          "source": "input",
          "operator": "eq",
          "value": "new_password",
          "description": "Confirm matches new password"
        }
      ],
      "then": [
        {
          "action": "set_field",
          "target": "password_hash",
          "value": "bcrypt(new_password, 12)",
          "description": "Hash new password with bcrypt (12 rounds)"
        },
        {
          "action": "set_field",
          "target": "token_used",
          "value": true,
          "description": "Mark token as used (single-use)"
        },
        {
          "action": "invalidate",
          "target": "sessions",
          "scope": "all_sessions",
          "description": "Invalidate ALL active sessions for this user"
        },
        {
          "action": "notify",
          "channel": "email",
          "template": "password_changed_confirmation",
          "to": "user",
          "description": "Confirm password was changed"
        },
        {
          "action": "emit_event",
          "event": "password_reset.success",
          "payload": [
            "user_id",
            "email",
            "timestamp",
            "ip_address"
          ]
        }
      ],
      "result": "redirect to /login with \"Password reset successful. Please sign in with your new password.\"",
      "error": "RESET_PASSWORD_WEAK"
    }
  },
  "errors": [
    {
      "code": "RESET_VALIDATION_ERROR",
      "status": 422,
      "message": "Please check your input and try again",
      "retry": true
    },
    {
      "code": "RESET_TOKEN_INVALID",
      "status": 400,
      "message": "This reset link is invalid. Please request a new one.",
      "retry": false,
      "redirect": "password-reset"
    },
    {
      "code": "RESET_TOKEN_EXPIRED",
      "status": 400,
      "message": "This reset link has expired. Please request a new one.",
      "retry": false,
      "redirect": "password-reset"
    },
    {
      "code": "RESET_PASSWORD_WEAK",
      "status": 422,
      "message": "Password does not meet security requirements",
      "retry": true
    },
    {
      "code": "RESET_PASSWORD_MISMATCH",
      "status": 422,
      "message": "Passwords do not match",
      "retry": true
    },
    {
      "code": "RESET_PASSWORD_REUSED",
      "status": 422,
      "message": "Please choose a password you haven't used recently",
      "retry": true
    },
    {
      "code": "RESET_RATE_LIMITED",
      "status": 429,
      "message": "Please wait before requesting another reset",
      "retry": false
    }
  ],
  "events": [
    {
      "name": "password_reset.requested",
      "description": "User requested a password reset email",
      "payload": [
        "user_id",
        "email",
        "timestamp",
        "ip_address",
        "token_expires_at"
      ]
    },
    {
      "name": "password_reset.success",
      "description": "Password was successfully changed",
      "payload": [
        "user_id",
        "email",
        "timestamp",
        "ip_address"
      ]
    },
    {
      "name": "password_reset.token_invalid",
      "description": "Invalid or tampered token was submitted",
      "payload": [
        "token_hash",
        "timestamp",
        "ip_address"
      ]
    },
    {
      "name": "password_reset.token_expired",
      "description": "Expired token was submitted",
      "payload": [
        "user_id",
        "timestamp",
        "ip_address"
      ]
    },
    {
      "name": "password_reset.email_not_found",
      "description": "Reset requested for non-existent email",
      "payload": [
        "email",
        "timestamp",
        "ip_address"
      ]
    }
  ],
  "related": [
    {
      "feature": "login",
      "type": "required",
      "reason": "After reset, user logs in with new password",
      "ui_link": "Back to sign in",
      "ui_link_position": "below_form"
    },
    {
      "feature": "signup",
      "type": "recommended",
      "reason": "User might not have an account yet",
      "ui_link": "Don't have an account? Sign up",
      "ui_link_position": "below_form"
    },
    {
      "feature": "email-verification",
      "type": "optional",
      "reason": "Uses similar email token pattern — can share infrastructure"
    }
  ],
  "agi": {
    "goals": [
      {
        "id": "reliable_password_reset",
        "description": "Allow users to reset their password via email verification",
        "success_metrics": [
          {
            "metric": "unauthorized_access_rate",
            "target": "0%",
            "measurement": "Failed authorization attempts that succeed"
          },
          {
            "metric": "response_time_p95",
            "target": "< 500ms",
            "measurement": "95th percentile response time"
          }
        ],
        "constraints": [
          {
            "type": "security",
            "description": "Follow OWASP security recommendations",
            "negotiable": false
          },
          {
            "type": "security",
            "description": "Sensitive fields must be encrypted at rest and never logged in plaintext",
            "negotiable": false
          }
        ]
      }
    ],
    "autonomy": {
      "level": "supervised",
      "human_checkpoints": [
        "before modifying sensitive data fields"
      ],
      "escalation_triggers": [
        "error_rate > 5",
        "consecutive_failures > 3"
      ]
    },
    "safety": {
      "action_permissions": [
        {
          "action": "request_rate_limited",
          "permission": "autonomous"
        },
        {
          "action": "reset_requested_unknown_email",
          "permission": "autonomous"
        },
        {
          "action": "reset_requested",
          "permission": "autonomous"
        },
        {
          "action": "token_invalid",
          "permission": "autonomous"
        },
        {
          "action": "token_expired",
          "permission": "autonomous"
        },
        {
          "action": "password_reused",
          "permission": "autonomous"
        },
        {
          "action": "password_reset_success",
          "permission": "autonomous"
        }
      ]
    },
    "tradeoffs": [
      {
        "prefer": "security",
        "over": "performance",
        "reason": "authentication must prioritize preventing unauthorized access"
      }
    ],
    "verification": {
      "invariants": [
        "sensitive fields are never logged in plaintext",
        "all data access is authenticated and authorized",
        "error messages never expose internal system details"
      ]
    },
    "coordination": {
      "protocol": "request_response",
      "consumes": [
        {
          "capability": "login",
          "from": "login",
          "fallback": "fail"
        }
      ]
    }
  },
  "ui_hints": {
    "screens": {
      "request": {
        "layout": "single_column_centered",
        "max_width": "420px",
        "show_logo": true,
        "title": "Reset your password",
        "subtitle": "Enter your email and we'll send you a reset link",
        "fields_order": [
          "email"
        ],
        "actions": {
          "primary": {
            "label": "Send reset link",
            "type": "submit",
            "full_width": true
          }
        },
        "links": [
          {
            "label": "Back to sign in",
            "target": "login",
            "position": "below_form"
          }
        ]
      },
      "reset": {
        "layout": "single_column_centered",
        "max_width": "420px",
        "show_logo": true,
        "title": "Set new password",
        "subtitle": "Enter your new password below",
        "fields_order": [
          "new_password",
          "confirm_new_password"
        ],
        "actions": {
          "primary": {
            "label": "Reset password",
            "type": "submit",
            "full_width": true
          }
        }
      }
    },
    "accessibility": {
      "autofocus_request": "email",
      "autofocus_reset": "new_password",
      "autocomplete": {
        "new_password": "new-password",
        "confirm_new_password": "new-password"
      },
      "aria_live_region": true
    },
    "loading": {
      "disable_button": true,
      "show_spinner": true,
      "prevent_double_submit": true
    },
    "password_strength": {
      "show_meter": true,
      "show_requirements": true
    }
  },
  "extensions": {
    "nextjs": {
      "routes": {
        "request": "/forgot-password",
        "reset": "/reset-password"
      },
      "layout": "(auth)",
      "server_action": true
    },
    "payload_cms": {
      "collection": "users",
      "auth": {
        "forgotPassword": {
          "generateEmailHTML": true
        },
        "verify": false
      }
    },
    "laravel": {
      "routes": {
        "request": "/forgot-password",
        "reset": "/reset-password/{token}"
      },
      "middleware": [
        "guest"
      ],
      "notification": "ResetPassword",
      "broker": "users"
    },
    "express": {
      "routes": {
        "request": "/api/auth/forgot-password",
        "reset": "/api/auth/reset-password"
      },
      "middleware": [
        "rate-limit",
        "cors"
      ]
    }
  }
}