diff --git a/joj3_config_generator/convert.py b/joj3_config_generator/convert.py
index 4b4d325..b4b1ebc 100644
--- a/joj3_config_generator/convert.py
+++ b/joj3_config_generator/convert.py
@@ -2,7 +2,12 @@ from typing import List
 
 from joj3_config_generator.models import joj1, repo, result, task
 from joj3_config_generator.lib.repo import getHealthcheckConfig, getTeapotConfig
-from joj3_config_generator.lib.task import fix_comment, fix_keyword, fix_result_detail
+from joj3_config_generator.lib.task import (
+    fix_comment,
+    fix_diff,
+    fix_keyword,
+    fix_result_detail,
+)
 from joj3_config_generator.models import (
     Cmd,
     CmdFile,
@@ -67,7 +72,7 @@ def convert(repo_conf: repo.Config, task_conf: task.Config) -> result.Config:
         conf_stage = fix_comment(task_stage, conf_stage)
         conf_stage = fix_keyword(task_stage, conf_stage)
         # TODO: fix diff parser here
-
+        conf_stage = fix_diff(task_stage, conf_stage)
         result_conf.stage.stages.append(conf_stage)
 
     return result_conf
diff --git a/joj3_config_generator/lib/task.py b/joj3_config_generator/lib/task.py
index 900749b..d8d2f78 100644
--- a/joj3_config_generator/lib/task.py
+++ b/joj3_config_generator/lib/task.py
@@ -1,27 +1,32 @@
+import rtoml
+
 from joj3_config_generator.models.result import Stage as ResultStage
 from joj3_config_generator.models.task import Stage as TaskStage
 
 
 def fix_keyword(task_stage: TaskStage, conf_stage: ResultStage) -> ResultStage:
     keyword_parser = ["clangtidy", "keyword", "cppcheck"]  # TODO: may add cpplint
-    for parser in task_stage.parsers:
-        if parser in keyword_parser:
-            keyword_parser_ = next(p for p in conf_stage.parsers if p.name == parser)
-            keyword_weight = []
-            if getattr(task_stage, parser, None) is not None:
-                for _, keyword in enumerate(getattr(task_stage, parser).keyword):
-                    keyword_weight.append({"keyword": [keyword], "score": 0})
-                for idx, weight in enumerate(getattr(task_stage, parser).weight):
-                    keyword_weight[idx]["score"] = weight
+    if task_stage.parsers is not None:
+        for parser in task_stage.parsers:
+            if parser in keyword_parser:
+                keyword_parser_ = next(
+                    p for p in conf_stage.parsers if p.name == parser
+                )
+                keyword_weight = []
+                if getattr(task_stage, parser, None) is not None:
+                    for _, keyword in enumerate(getattr(task_stage, parser).keyword):
+                        keyword_weight.append({"keyword": [keyword], "score": 0})
+                    for idx, weight in enumerate(getattr(task_stage, parser).weight):
+                        keyword_weight[idx]["score"] = weight
 
-            keyword_parser_.with_.update({"match": keyword_weight})
-        else:
-            continue
+                keyword_parser_.with_.update({"match": keyword_weight})
+            else:
+                continue
     return conf_stage
 
 
 def fix_result_detail(task_stage: TaskStage, conf_stage: ResultStage) -> ResultStage:
-    if "result-detail" in task_stage.parsers:
+    if (task_stage.parsers is not None) and ("result-detail" in task_stage.parsers):
         result_detail_parser = next(
             p for p in conf_stage.parsers if p.name == "result-detail"
         )
@@ -57,13 +62,21 @@ def fix_comment(task_stage: TaskStage, conf_stage: ResultStage) -> ResultStage:
         "result-status",
         "cpplint",
     ]  # FIXME: determine where cpplint should be
-    for parser in task_stage.parsers:
-        if parser in comment_parser:
-            comment_parser_ = next(p for p in conf_stage.parsers if p.name == parser)
-            if getattr(task_stage, parser.replace("-", "_"), None) is not None:
-                comment_parser_.with_.update(
-                    getattr(task_stage, parser.replace("-", "_"))
+    if task_stage.parsers is not None:
+        for parser in task_stage.parsers:
+            if parser in comment_parser:
+                comment_parser_ = next(
+                    p for p in conf_stage.parsers if p.name == parser
                 )
-        else:
-            continue
+                if getattr(task_stage, parser.replace("-", "_"), None) is not None:
+                    comment_parser_.with_.update(
+                        getattr(task_stage, parser.replace("-", "_"))
+                    )
+            else:
+                continue
+    return conf_stage
+
+
+def fix_diff(task_stage: TaskStage, conf_stage: ResultStage) -> ResultStage:
+
     return conf_stage
diff --git a/joj3_config_generator/models/task.py b/joj3_config_generator/models/task.py
index ac4bd28..d5eefac 100644
--- a/joj3_config_generator/models/task.py
+++ b/joj3_config_generator/models/task.py
@@ -1,7 +1,7 @@
 from datetime import datetime
-from typing import List, Optional
+from typing import Any, Dict, Optional, Type, List
 
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, root_validator
 
 
 class ParserResultDetail(BaseModel):
@@ -21,6 +21,17 @@ class ParserKeyword(BaseModel):
     weight: Optional[list[int]] = []
 
 
+class Outputs(BaseModel):
+    score: Optional[int] = 0
+    ignorespaces: Optional[bool] = False
+    hide: Optional[bool] = False
+    forcequit: Optional[bool] = True
+
+
+class ParserDiff(BaseModel):
+    output: Optional[Outputs] = Outputs()
+
+
 class Files(BaseModel):
     import_: Optional[List[str]] = Field(serialization_alias="import", validation_alias="import")
     export: Optional[List[str]]
@@ -35,11 +46,11 @@ class Limit(BaseModel):
 
 
 class Stage(BaseModel):
-    name: str  # Stage name
-    command: str  # Command to run
+    name: Optional[str] = None  # Stage name
+    command: Optional[str] = None  # Command to run
     files: Optional[Files] = None
     score: Optional[int] = 0
-    parsers: list[str]  # list of parsers
+    parsers: Optional[list[str]] = []  # list of parsers
     limit: Optional[Limit] = None
     dummy: Optional[ParserDummy] = ParserDummy()
     result_status: Optional[ParserDummy] = Field(ParserDummy(), alias="result-status")
@@ -52,6 +63,20 @@ class Stage(BaseModel):
     result_detail: Optional[ParserResultDetail] = Field(
         ParserResultDetail(), alias="result-detail"
     )
+    skip: Optional[list[str]] = []
+    diff: Optional[ParserDiff] = ParserDiff()
+    cases: Optional[Dict[str, "Stage"]] = {}
+
+    class Config:
+        extra = "allow"
+
+    @root_validator(pre=True)
+    def gather_cases(cls: Type["Stage"], values: Dict[str, Any]) -> Dict[str, Any]:
+        cases = {k: v for k, v in values.items() if k.startswith("case")}
+        for key in cases:
+            values.pop(key)
+        values["cases"] = {k: Stage(**v) for k, v in cases.items()}
+        return values
 
 
 class Release(BaseModel):
diff --git a/tests/convert/basic/task.json b/tests/convert/basic/task.json
index f6f5ea7..d80e014 100644
--- a/tests/convert/basic/task.json
+++ b/tests/convert/basic/task.json
@@ -1,6 +1,6 @@
 {
-    "name": "Homework 1 exercise 2",
-    "logPath": "Homework-1-exercise-2.log",
+    "name": "h4 ex1",
+    "logPath": "h4-ex1.log",
     "expireUnixTimestamp": 1728748740,
     "stage": {
         "sandboxExecServer": "172.17.0.1:5051",
@@ -18,7 +18,7 @@
                                 "/<function",
                                 "get_temp_directory",
                                 "at",
-                                "0x7fd3bed4f1a0>/repo-health-checker",
+                                "0x7f932c5e31a0>/repo-health-checker",
                                 "-root=.",
                                 "-repoSize=50.5",
                                 "-meta=main.py",
@@ -71,8 +71,8 @@
                             "cpuRateLimit": 0,
                             "cpuSetLimit": "",
                             "copyIn": {
-                                "//tmp/repo-checker-9z61g608/repo-health-checker": {
-                                    "src": "//tmp/repo-checker-19d98f6u/repo-health-checker",
+                                "//tmp/repo-checker-tw0902sa/repo-health-checker": {
+                                    "src": "//tmp/repo-checker-4sy3g0ro/repo-health-checker",
                                     "content": null,
                                     "fileId": null,
                                     "name": null,
@@ -234,11 +234,6 @@
                     {
                         "name": "result-detail",
                         "with": {
-                            "time": false,
-                            "mem": false,
-                            "stdout": false,
-                            "stderr": true,
-                            "exitstatus": true,
                             "score": 0,
                             "comment": "",
                             "showFiles": [
@@ -379,11 +374,6 @@
                     {
                         "name": "result-detail",
                         "with": {
-                            "time": false,
-                            "mem": false,
-                            "stdout": false,
-                            "stderr": true,
-                            "exitstatus": false,
                             "score": 0,
                             "comment": "",
                             "showFiles": [
@@ -571,11 +561,6 @@
                     {
                         "name": "result-detail",
                         "with": {
-                            "time": false,
-                            "mem": false,
-                            "stdout": true,
-                            "stderr": false,
-                            "exitstatus": true,
                             "score": 0,
                             "comment": "",
                             "showFiles": [
@@ -713,11 +698,6 @@
                     {
                         "name": "result-detail",
                         "with": {
-                            "time": false,
-                            "mem": false,
-                            "stdout": false,
-                            "stderr": true,
-                            "exitstatus": true,
                             "score": 0,
                             "comment": "",
                             "showFiles": [
@@ -820,11 +800,6 @@
                     {
                         "name": "result-detail",
                         "with": {
-                            "time": false,
-                            "mem": false,
-                            "stdout": true,
-                            "stderr": false,
-                            "exitstatus": true,
                             "score": 0,
                             "comment": "",
                             "showFiles": [
@@ -921,11 +896,6 @@
                     {
                         "name": "result-detail",
                         "with": {
-                            "time": true,
-                            "mem": true,
-                            "stdout": false,
-                            "stderr": true,
-                            "exitstatus": true,
                             "score": 0,
                             "comment": "",
                             "showFiles": [
@@ -1022,11 +992,6 @@
                     {
                         "name": "result-detail",
                         "with": {
-                            "time": true,
-                            "mem": true,
-                            "stdout": false,
-                            "stderr": true,
-                            "exitstatus": true,
                             "score": 0,
                             "comment": "",
                             "showFiles": [
@@ -1042,9 +1007,9 @@
         ]
     },
     "teapot": {
-        "logPath": "Homework-1-exercise-2-joint-teapot-debug.log",
-        "scoreboardPath": "Homework-1-exercise-2-scoreboard.csv",
-        "failedTablePath": "Homework-1-exercise-2-failed-table.md",
+        "logPath": "h4-ex1-joint-teapot-debug.log",
+        "scoreboardPath": "h4-ex1-scoreboard.csv",
+        "failedTablePath": "h4-ex1-failed-table.md",
         "gradingRepoName": "engr151-joj",
         "skipIssue": false,
         "skipScoreboard": false,
diff --git a/tests/convert/basic/task.toml b/tests/convert/basic/task.toml
index 48dfd07..ec4cdfc 100644
--- a/tests/convert/basic/task.toml
+++ b/tests/convert/basic/task.toml
@@ -1,5 +1,5 @@
 # general task configuration
-task="Homework 1 exercise 2" # task name
+task="h4 ex1" # task name
 
 release.deadline = 2024-10-12 23:59:00+08:00
 release.stages = [ "compile" ]