diff --git a/joj3_config_generator/convert.py b/joj3_config_generator/convert.py
index f19a42d..f884f22 100644
--- a/joj3_config_generator/convert.py
+++ b/joj3_config_generator/convert.py
@@ -1,5 +1,10 @@
 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,
@@ -16,7 +21,8 @@ from joj3_config_generator.models import (
 
 
 # FIXME: LLM generated convert function, only for demostration
-def convert(repo_conf: repo.Config, task_conf: task.Config) -> result.Config:
+def convert(repo_conf: Repo, task_conf: Task) -> ResultConfig:
+    print(task_conf)
     # Create the base ResultConf object
     # FIXME: wrap things in functions
     result_conf = ResultConfig(
@@ -54,9 +60,12 @@ def convert(repo_conf: repo.Config, task_conf: task.Config) -> result.Config:
             and (task_stage.files is not None)
             else []
         )
+        # TODO: the global limit field
         executor_with_config = ExecutorWithConfig(
             default=Cmd(
-                args=task_stage.command.split(),
+                args=(
+                    task_stage.command.split() if task_stage.command is not None else []
+                ),
                 copy_in={
                     file: CmdFile(src=f"/home/tt/.config/joj/{file}")
                     for file in copy_in_files
@@ -71,22 +80,28 @@ def convert(repo_conf: repo.Config, task_conf: task.Config) -> result.Config:
                 if file not in cached:
                     cached.append(file)
         conf_stage = Stage(
-            name=task_stage.name,
+            name=task_stage.name if task_stage.name is not None else "",
             # TODO: we may have cq in future
-            group="joj" if "judge" in task_stage.name else None,
+            group=(
+                "joj"
+                if (task_stage.name is not None) and ("judge" in task_stage.name)
+                else None
+            ),
             executor=ExecutorConfig(
                 name="sandbox",
                 with_=executor_with_config,
             ),
-            parsers=[
-                ParserConfig(name=parser, with_={}) for parser in task_stage.parsers
-            ],
+            parsers=(
+                [ParserConfig(name=parser, with_={}) for parser in task_stage.parsers]
+                if task_stage.parsers is not None
+                else []
+            ),
         )
         conf_stage = fix_result_detail(task_stage, conf_stage)
         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/main.py b/joj3_config_generator/main.py
index 9e07455..3f388ac 100644
--- a/joj3_config_generator/main.py
+++ b/joj3_config_generator/main.py
@@ -8,9 +8,9 @@ import typer
 import yaml
 
 from joj3_config_generator.convert import convert as convert_conf
-from joj3_config_generator.convert import convert_joj1 as convert_joj1_conf
-from joj3_config_generator.lib.task import remove_nulls
-from joj3_config_generator.models import joj1, repo, result, task
+
+# from joj3_config_generator.lib.task import get_processed_task_obj
+from joj3_config_generator.models import Repo, Task
 from joj3_config_generator.utils.logger import logger
 
 app = typer.Typer(add_completion=False)
diff --git a/joj3_config_generator/models/task.py b/joj3_config_generator/models/task.py
index 31fd3f2..9c4602c 100644
--- a/joj3_config_generator/models/task.py
+++ b/joj3_config_generator/models/task.py
@@ -1,8 +1,7 @@
 from datetime import datetime
-from typing import Any, Dict, List, Optional, Type
+from typing import Any, Dict, Optional, Type
 
 from pydantic import BaseModel, Field, root_validator
-from pydantic import BaseModel, Field, root_validator
 
 
 class ParserResultDetail(BaseModel):
@@ -22,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([], alias="import")
     export: Optional[list[str]] = []
@@ -35,11 +45,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 +62,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):