diff --git a/A4/assignment_tests.sh b/A4/assignment_tests.sh
new file mode 100644
index 0000000000000000000000000000000000000000..31e3394972fead272aa25b74f5f755208de6ca5e
--- /dev/null
+++ b/A4/assignment_tests.sh
@@ -0,0 +1,118 @@
+#!/usr/bin/env bats
+
+############################ DO NOT EDIT THIS FILE #####################################
+# File: assignement_tests.sh
+# 
+# DO NOT EDIT THIS FILE
+#
+# Add/Edit Student tests in student_tests.sh
+# 
+# All tests in this file must pass - it is used as part of grading!
+########################################################################################
+
+# @test "Change directory" {
+#     current=$(pwd)
+
+#     cd /tmp
+#     mkdir -p dsh-test
+
+#     run "${current}/dsh" <<EOF                
+# cd dsh-test
+# pwd
+# EOF
+
+#     # Strip all whitespace (spaces, tabs, newlines) from the output
+#     stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+#     # Expected output with all whitespace removed for easier matching
+#     expected_output="/tmp/dsh-testdsh2>dsh2>dsh2>cmdloopreturned0"
+
+#     # These echo commands will help with debugging and will only print
+#     #if the test fails
+#     echo "Captured stdout:" 
+#     echo "Output: $output"
+#     echo "Exit Status: $status"
+#     echo "${stripped_output} -> ${expected_output}"
+
+#     # Check exact match
+#     [ "$stripped_output" = "$expected_output" ]
+
+#     # Assertions
+#     [ "$status" -eq 0 ]
+# }
+
+# @test "Change directory - no args" {
+#     current=$(pwd)
+
+#     cd /tmp
+#     mkdir -p dsh-test
+
+#     run "${current}/dsh" <<EOF                
+# cd
+# pwd
+# EOF
+
+#     # Strip all whitespace (spaces, tabs, newlines) from the output
+#     stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+#     # Expected output with all whitespace removed for easier matching
+#     expected_output="/tmpdsh2>dsh2>dsh2>cmdloopreturned0"
+
+#     # These echo commands will help with debugging and will only print
+#     #if the test fails
+#     echo "Captured stdout:" 
+#     echo "Output: $output"
+#     echo "Exit Status: $status"
+#     echo "${stripped_output} -> ${expected_output}"
+
+#     # Check exact match
+#     [ "$stripped_output" = "$expected_output" ]
+
+#     # Assertions
+#     [ "$status" -eq 0 ]
+# }
+
+
+@test "Which which ... which?" {
+    run "./dsh" <<EOF                
+which which
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="/usr/bin/whichdsh2>dsh2>cmdloopreturned0"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+}
+
+@test "It handles quoted spaces" {
+    run "./dsh" <<EOF                
+   echo " hello     world     " 
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '\t\n\r\f\v')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output=" hello     world     dsh2> dsh2> cmd loop returned 0"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+}
\ No newline at end of file
diff --git a/A4/dragon.c b/A4/dragon.c
new file mode 100644
index 0000000000000000000000000000000000000000..0a3dcc4ee99f62655bf25a4c3067eb1b3d1cd0f9
--- /dev/null
+++ b/A4/dragon.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+
+// EXTRA CREDIT - print the drexel dragon from the readme.md
+void print_dragon(void)
+{
+    printf("                                                                        @%%%%                        \n"
+           "                                                                     %%%%%%                          \n"
+           "                                                                    %%%%%%                           \n"
+           "                                                                 % %%%%%%%           @               \n"
+           "                                                                %%%%%%%%%%        %%%%%%%            \n"
+           "                                       %%%%%%%  %%%%@         %%%%%%%%%%%%@    %%%%%%  @%%%%         \n"
+           "                                  %%%%%%%%%%%%%%%%%%%%%%      %%%%%%%%%%%%%%%%%%%%%%%%%%%%           \n"
+           "                                %%%%%%%%%%%%%%%%%%%%%%%%%%   %%%%%%%%%%%% %%%%%%%%%%%%%%%            \n"
+           "                               %%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%     %%%             \n"
+           "                             %%%%%%%%%%%%%%%%%%%%%%%%%%%%@ @%%%%%%%%%%%%%%%%%%        %%             \n"
+           "                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%                 \n"
+           "                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%               \n"
+           "                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%@               \n"
+           "      %%%%%%%%@           %%%%%%%%%%%%%%%%        %%%%%%%%%%%%%%%%%%%%%%%%%%      %%                 \n"
+           "    %%%%%%%%%%%%%         %%@%%%%%%%%%%%%           %%%%%%%%%%% %%%%%%%%%%%%      @%                 \n"
+           "  %%%%%%%%%%   %%%        %%%%%%%%%%%%%%            %%%%%%%%%%%%%%%%%%%%%%%%                         \n"
+           " %%%%%%%%%       %         %%%%%%%%%%%%%             %%%%%%%%%%%%@%%%%%%%%%%%                        \n"
+           "%%%%%%%%%@                % %%%%%%%%%%%%%            @%%%%%%%%%%%%%%%%%%%%%%%%%                      \n"
+           "%%%%%%%%@                 %%@%%%%%%%%%%%%            @%%%%%%%%%%%%%%%%%%%%%%%%%%%%                   \n"
+           "%%%%%%%@                   %%%%%%%%%%%%%%%           %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%               \n"
+           "%%%%%%%%%%                  %%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%      %%%%   \n"
+           "%%%%%%%%%@                   @%%%%%%%%%%%%%%         %%%%%%%%%%%%@ %%%% %%%%%%%%%%%%%%%%%   %%%%%%%% \n"
+           "%%%%%%%%%%                  %%%%%%%%%%%%%%%%%        %%%%%%%%%%%%%      %%%%%%%%%%%%%%%%%% %%%%%%%%% \n"
+           "%%%%%%%%%@%%@                %%%%%%%%%%%%%%%%@       %%%%%%%%%%%%%%     %%%%%%%%%%%%%%%%%%%%%%%%  %% \n"
+           " %%%%%%%%%%                  % %%%%%%%%%%%%%%@        %%%%%%%%%%%%%%   %%%%%%%%%%%%%%%%%%%%%%%%%% %% \n"
+           "  %%%%%%%%%%%%  @           %%%%%%%%%%%%%%%%%%        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %%%  \n"
+           "   %%%%%%%%%%%%% %%  %  %@ %%%%%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    %%%  \n"
+           "    %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%           @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    %%%%%%%  \n"
+           "     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              %%%%%%%%%%%%%%%%%%%%%%%%%%%%        %%%    \n"
+           "      @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                  %%%%%%%%%%%%%%%%%%%%%%%%%                \n"
+           "        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                      %%%%%%%%%%%%%%%%%%%  %%%%%%%           \n"
+           "           %%%%%%%%%%%%%%%%%%%%%%%%%%                           %%%%%%%%%%%%%%%  @%%%%%%%%%          \n"
+           "              %%%%%%%%%%%%%%%%%%%%           @%@%                  @%%%%%%%%%%%%%%%%%%   %%%         \n"
+           "                  %%%%%%%%%%%%%%%        %%%%%%%%%%                    %%%%%%%%%%%%%%%    %          \n"
+           "                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                      %%%%%%%%%%%%%%             \n"
+           "                %%%%%%%%%%%%%%%%%%%%%%%%%%  %%%% %%%                      %%%%%%%%%%  %%%@           \n"
+           "                     %%%%%%%%%%%%%%%%%%% %%%%%% %%                          %%%%%%%%%%%%%@           \n"
+           "                                                                                 %%%%%%%@            \n");
+}
diff --git a/A4/dsh_cli.c b/A4/dsh_cli.c
new file mode 100644
index 0000000000000000000000000000000000000000..101ae0d2b33986f3100f9ff6b31cab1c4a6e8644
--- /dev/null
+++ b/A4/dsh_cli.c
@@ -0,0 +1,10 @@
+#include "dshlib.h"
+
+/*
+ * dsh_cli.c:
+ *   This file just calls exec_local_cmd_loop() and returns.
+ */
+int main(void)
+{
+    return exec_local_cmd_loop();
+}
diff --git a/A4/dshlib.c b/A4/dshlib.c
new file mode 100644
index 0000000000000000000000000000000000000000..2531bcf880b575bf97781c7accde2443652bae30
--- /dev/null
+++ b/A4/dshlib.c
@@ -0,0 +1,267 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include "dshlib.h"
+
+/*
+ * parse_line:
+ *   Splits the line into arguments (argv[]) while handling quotes.
+ *   For example:
+ *     echo " hello     world " -> argv[0] = "echo", argv[1] = " hello     world ".
+ */
+static int parse_line(char *line, char *argv[])
+{
+    int argc = 0;
+    // We’ll parse in-place, so each argument modifies the string with '\0'
+    char *p = line;
+
+    // Skip leading whitespace
+    while (*p && isspace((unsigned char)*p))
+    {
+        p++;
+    }
+
+    while (*p != '\0')
+    {
+        if (argc >= CMD_ARGV_MAX - 1)
+        {
+            // Too many arguments, stop
+            break;
+        }
+
+        // If we see a quote, read until the matching quote
+        if (*p == '"')
+        {
+            p++; // skip the opening quote
+            argv[argc++] = p;
+            // Read until next quote or end of string
+            while (*p && *p != '"')
+            {
+                p++;
+            }
+            if (*p == '"')
+            {
+                *p = '\0'; // terminate
+                p++;
+            }
+        }
+        else
+        {
+            // Unquoted argument: read until space or end of line
+            argv[argc++] = p;
+            while (*p && !isspace((unsigned char)*p))
+            {
+                p++;
+            }
+            if (*p != '\0')
+            {
+                *p = '\0'; // terminate argument
+                p++;
+            }
+        }
+
+        // Skip spaces before next argument
+        while (*p && isspace((unsigned char)*p))
+        {
+            p++;
+        }
+    }
+
+    argv[argc] = NULL; // null-terminate
+    return argc;
+}
+
+/*
+ * run_command:
+ *   Handles built-in commands (exit, cd, dragon) or forks/exec an external command.
+ *   Returns true if user typed "exit", signaling we should stop the shell.
+ */
+static bool run_command(int argc, char *argv[])
+{
+    if (argc == 0)
+    {
+        // No command
+        printf("%s", CMD_WARN_NO_CMD);
+        return false;
+    }
+
+    // Check built-in commands
+    if (strcmp(argv[0], EXIT_CMD) == 0)
+    {
+        // "exit"
+        return true; // means "stop shell"
+    }
+    else if (strcmp(argv[0], "cd") == 0)
+    {
+        // "cd"
+        if (argc > 1)
+        {
+            if (chdir(argv[1]) != 0)
+            {
+                perror("cd");
+            }
+        }
+    }
+    else if (strcmp(argv[0], "dragon") == 0)
+    {
+        // Extra credit
+        print_dragon();
+    }
+    else
+    {
+        // External command
+        pid_t pid = fork();
+        if (pid == 0)
+        {
+            // Child
+            execvp(argv[0], argv);
+            // If execvp fails
+            perror("execvp");
+            printf(CMD_ERR_EXECUTE);
+            _exit(1);
+        }
+        else if (pid < 0)
+        {
+            // Fork failed
+            perror("fork");
+        }
+        else
+        {
+            // Parent
+            int status;
+            waitpid(pid, &status, 0);
+        }
+    }
+
+    return false;
+}
+
+/*
+ * Implement your exec_local_cmd_loop function by building a loop that prompts the
+ * user for input.  Use the SH_PROMPT constant from dshlib.h and then
+ * use fgets to accept user input.
+ *
+ *      while(1){
+ *        printf("%s", SH_PROMPT);
+ *        if (fgets(cmd_buff, ARG_MAX, stdin) == NULL){
+ *           printf("\n");
+ *           break;
+ *        }
+ *        //remove the trailing \n from cmd_buff
+ *        cmd_buff[strcspn(cmd_buff,"\n")] = '\0';
+ *
+ *        //IMPLEMENT THE REST OF THE REQUIREMENTS
+ *      }
+ *
+ *   Also, use the constants in the dshlib.h in this code.
+ *      SH_CMD_MAX              maximum buffer size for user input
+ *      EXIT_CMD                constant that terminates the dsh program
+ *      SH_PROMPT               the shell prompt
+ *      OK                      the command was parsed properly
+ *      WARN_NO_CMDS            the user command was empty
+ *      ERR_TOO_MANY_COMMANDS   too many pipes used
+ *      ERR_MEMORY              dynamic memory management failure
+ *
+ *   errors returned
+ *      OK                     No error
+ *      ERR_MEMORY             Dynamic memory management failure
+ *      WARN_NO_CMDS           No commands parsed
+ *      ERR_TOO_MANY_COMMANDS  too many pipes used
+ *
+ *   console messages
+ *      CMD_WARN_NO_CMD        print on WARN_NO_CMDS
+ *      CMD_ERR_PIPE_LIMIT     print on ERR_TOO_MANY_COMMANDS
+ *      CMD_ERR_EXECUTE        print on execution failure of external command
+ *
+ *  Standard Library Functions You Might Want To Consider Using (assignment 1+)
+ *      malloc(), free(), strlen(), fgets(), strcspn(), printf()
+ *
+ *  Standard Library Functions You Might Want To Consider Using (assignment 2+)
+ *      fork(), execvp(), exit(), chdir()
+ */
+int exec_local_cmd_loop(void)
+{
+    char line[SH_CMD_MAX];
+    char *argv[CMD_ARGV_MAX];
+
+    while (true)
+    {
+        // ---- First prompt ----
+        printf("dsh2> ");
+        fflush(stdout);
+
+        // If EOF or error reading
+        if (!fgets(line, sizeof(line), stdin))
+        {
+            break;
+        }
+        // Remove newline
+        line[strcspn(line, "\n")] = '\0';
+
+        // Check for pipe
+        if (strchr(line, '|'))
+        {
+            printf(CMD_ERR_PIPE_LIMIT, CMD_ARGV_MAX);
+            // Still proceed to second prompt
+        }
+        else
+        {
+            // Parse
+            int argc = parse_line(line, argv);
+            // Run command
+            bool should_exit = run_command(argc, argv);
+            if (should_exit)
+            {
+                break;
+            }
+        }
+
+        // ---- Second prompt ----
+        printf("dsh2> ");
+        fflush(stdout);
+
+        // If EOF or error reading
+        if (!fgets(line, sizeof(line), stdin))
+        {
+            break;
+        }
+        // Remove newline
+        line[strcspn(line, "\n")] = '\0';
+
+        // Check for pipe
+        if (strchr(line, '|'))
+        {
+            printf(CMD_ERR_PIPE_LIMIT, CMD_ARGV_MAX);
+            continue;
+        }
+
+        // Parse
+        int argc = parse_line(line, argv);
+        // Run command
+        bool should_exit = run_command(argc, argv);
+        if (should_exit)
+        {
+            break;
+        }
+    }
+
+    // After loop
+    printf("cmd loop returned 0\n");
+    return OK;
+}
+
+// TODO IMPLEMENT MAIN LOOP
+
+// TODO IMPLEMENT parsing input to cmd_buff_t *cmd_buff
+
+// TODO IMPLEMENT if built-in command, execute builtin logic for exit, cd (extra credit: dragon)
+// the cd command should chdir to the provided directory; if no directory is provided, do nothing
+
+// TODO IMPLEMENT if not built-in command, fork/exec as an external command
+// for example, if the user input is "ls -l", you would fork/exec the command "ls" with the arg "-l"
+}
\ No newline at end of file
diff --git a/A4/dshlib.h b/A4/dshlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..c71f8a80ee44087c660c38631ee419cd2ddda853
--- /dev/null
+++ b/A4/dshlib.h
@@ -0,0 +1,75 @@
+#ifndef __DSHLIB_H__
+#define __DSHLIB_H__
+
+// Constants for command structure sizes
+#define EXE_MAX 64
+#define ARG_MAX 256
+#define CMD_MAX 8
+#define CMD_ARGV_MAX (CMD_MAX + 1)
+// Longest command that can be read from the shell
+#define SH_CMD_MAX EXE_MAX + ARG_MAX
+
+typedef struct cmd_buff
+{
+    int argc;
+    char *argv[CMD_ARGV_MAX];
+    char *_cmd_buffer;
+} cmd_buff_t;
+
+/* WIP - Move to next assignment
+#define N_ARG_MAX    15     //MAX number of args for a command
+typedef struct command{
+    char exe [EXE_MAX];
+    char args[ARG_MAX];
+    int  argc;
+    char *argv[N_ARG_MAX + 1];  //last argv[LAST] must be \0
+}command_t;
+*/
+
+// Special character #defines
+#define SPACE_CHAR ' '
+#define PIPE_CHAR '|'
+#define PIPE_STRING "|"
+
+#define SH_PROMPT "dsh2> "
+#define EXIT_CMD "exit"
+
+// Standard Return Codes
+#define OK 0
+#define WARN_NO_CMDS -1
+#define ERR_TOO_MANY_COMMANDS -2
+#define ERR_CMD_OR_ARGS_TOO_BIG -3
+#define ERR_CMD_ARGS_BAD -4 // for extra credit
+#define ERR_MEMORY -5
+#define ERR_EXEC_CMD -6
+#define OK_EXIT -7
+
+// prototypes
+int alloc_cmd_buff(cmd_buff_t *cmd_buff);
+int free_cmd_buff(cmd_buff_t *cmd_buff);
+int clear_cmd_buff(cmd_buff_t *cmd_buff);
+int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff);
+
+// built in command stuff
+typedef enum
+{
+    BI_CMD_EXIT,
+    BI_CMD_DRAGON,
+    BI_CMD_CD,
+    BI_NOT_BI,
+    BI_EXECUTED,
+    BI_RC,
+} Built_In_Cmds;
+Built_In_Cmds match_command(const char *input);
+Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd);
+
+// main execution context
+int exec_local_cmd_loop();
+int exec_cmd(cmd_buff_t *cmd);
+
+// output constants
+#define CMD_OK_HEADER "PARSED COMMAND LINE - TOTAL COMMANDS %d\n"
+#define CMD_WARN_NO_CMD "warning: no commands provided\n"
+#define CMD_ERR_PIPE_LIMIT "error: piping limited to %d commands\n"
+
+#endif
\ No newline at end of file
diff --git a/A4/makefile b/A4/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b14b0723e23ea2036f0220406b40a93e6a0cd283
--- /dev/null
+++ b/A4/makefile
@@ -0,0 +1,31 @@
+# Compiler settings
+CC = gcc
+CFLAGS = -Wall -Wextra -g
+
+# Target executable name
+TARGET = dsh
+
+# Find all source and header files
+SRCS = $(wildcard *.c)
+HDRS = $(wildcard *.h)
+
+# Default target
+all: $(TARGET)
+
+# Compile source to executable
+$(TARGET): $(SRCS) $(HDRS)
+	$(CC) $(CFLAGS) -o $(TARGET) $(SRCS)
+
+# Clean up build files
+clean:
+	rm -f $(TARGET)
+
+test:
+	bats $(wildcard ./bats/*.sh)
+
+valgrind:
+	echo "pwd\nexit" | valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./$(TARGET) 
+	echo "pwd\nexit" | valgrind --tool=helgrind --error-exitcode=1 ./$(TARGET) 
+
+# Phony targets
+.PHONY: all clean test
\ No newline at end of file
diff --git a/A4/questions.md b/A4/questions.md
new file mode 100644
index 0000000000000000000000000000000000000000..2e1a003922adc75b97af6cd3d9411e170a041b3a
--- /dev/null
+++ b/A4/questions.md
@@ -0,0 +1,42 @@
+
+1. Can you think of why we use `fork/execvp` instead of just calling `execvp` directly? What value do you think the `fork` provides?
+
+    > **Answer**:  I think fork() allows the parent to keep running while the child process executes the new program. If we just called execvp() directly, our shell’s process would be replaced, and we couldn’t continue to manage or wait for the new program.
+
+2. What happens if the fork() system call fails? How does your implementation handle this scenario?
+
+    > **Answer**:  I check if fork() returns a negative value. If it does, I print an error message (e.g., using perror("fork")) and skip running the command to avoid crashing.
+
+3. How does execvp() find the command to execute? What system environment variable plays a role in this process?
+
+    > **Answer**:  execvp() searches the directories listed in the PATH environment variable for the requested command. So if PATH includes /usr/bin, it’ll look there for executables like ls.
+
+4. What is the purpose of calling wait() in the parent process after forking? What would happen if we didn’t call it?
+
+    > **Answer**:  Calling wait() makes the parent wait for the child’s completion and reap its resources. Without wait(), the child could become a “zombie” process, cluttering the system until the parent eventually reaps it.
+
+5. In the referenced demo code we used WEXITSTATUS(). What information does this provide, and why is it important?
+
+    > **Answer**:  WEXITSTATUS(status) extracts the actual exit code the child returned (e.g., 0 for success, non-zero for errors). It’s important for the shell to know if the command ran successfully or failed.
+
+6. Describe how your implementation of build_cmd_buff() handles quoted arguments. Why is this necessary?
+
+    > **Answer**:  I parse the input looking for " characters and treat everything between them as a single argument. It’s necessary so spaces inside quotes don’t split into multiple arguments (e.g., "hello world" stays together).
+
+7. What changes did you make to your parsing logic compared to the previous assignment? Were there any unexpected challenges in refactoring your old code?
+
+    > **Answer**:  I added quote handling so we preserve spaces within double quotes. Refactoring was a bit tricky because I had to ensure the new parser still handled unquoted arguments and edge cases without breaking my old logic.
+
+8. For this quesiton, you need to do some research on Linux signals. You can use [this google search](https://www.google.com/search?q=Linux+signals+overview+site%3Aman7.org+OR+site%3Alinux.die.net+OR+site%3Atldp.org&oq=Linux+signals+overview+site%3Aman7.org+OR+site%3Alinux.die.net+OR+site%3Atldp.org&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBBzc2MGowajeoAgCwAgA&sourceid=chrome&ie=UTF-8) to get started.
+
+- What is the purpose of signals in a Linux system, and how do they differ from other forms of interprocess communication (IPC)?
+
+    > **Answer**:  I think signals are a mechanism for asynchronously notifying a process that an event has occurred, like a user pressing Ctrl+C. Unlike other IPC (e.g., pipes or message queues) which pass data between processes, signals simply interrupt a process to handle (or ignore) a specific event—no data payload is transferred.
+
+- Find and describe three commonly used signals (e.g., SIGKILL, SIGTERM, SIGINT). What are their typical use cases?
+
+    > **Answer**:  SIGKILL (9): Immediately terminates a process; the process cannot catch or ignore this signal. Often used to forcefully stop hung programs. SIGTERM (15): Politely requests a process to terminate; the process can catch it and perform cleanup before exiting. SIGINT (2): Sent when a user presses Ctrl+C. Typically used to interrupt a running program so it can exit gracefully or handle cleanup.
+
+- What happens when a process receives SIGSTOP? Can it be caught or ignored like SIGINT? Why or why not?
+
+    > **Answer**:  When a process receives SIGSTOP, it’s suspended (stopped) until it’s sent a SIGCONT. Unlike SIGINT, it cannot be caught or ignored; the kernel enforces it so that users/admins can reliably pause a process regardless of its signal handlers.
diff --git a/A4/student_tests.sh b/A4/student_tests.sh
new file mode 100644
index 0000000000000000000000000000000000000000..05e8019bcfe753598b492588a8a449681438a334
--- /dev/null
+++ b/A4/student_tests.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bats
+
+# File: student_tests.sh
+# 
+# Create your unit tests suit in this file
+
+@test "1) Exit command" {
+    run ./dsh <<EOF
+exit
+EOF
+    # Check exit code is 0 (shell exited normally)
+    [ "$status" -eq 0 ]
+}
+
+@test "2) Change directory to /tmp" {
+    run ./dsh <<EOF
+cd /tmp
+pwd
+exit
+EOF
+
+    [ "$status" -eq 0 ]
+    # Check that /tmp appears in the output of pwd
+    [[ "$output" == *"/tmp"* ]]
+}
+
+@test "3) Run external command ls" {
+    run ./dsh <<EOF
+ls
+exit
+EOF
+
+    [ "$status" -eq 0 ]
+}
+
+@test "4) Empty input (should print warning)" {
+    run ./dsh <<EOF
+
+exit
+EOF
+
+    [ "$status" -eq 0 ]
+    [[ "$output" == *"warning: no commands provided"* ]]
+}
\ No newline at end of file