idea_world_labDEV JOURNAL
Freitag, 26. Juni 2026

Fluss der Übergabe von Quellcodeeingaben zur AST-Analyse

Erstellungsdatum: 26. Juni 2026

Zweck

Dieses Dokument ergänzt den einleitenden Teil von docs/roadmaps/2026-06-25-source-analysis-scoring-architecture.md.

Im Standard‑Roadmap wurde bereits folgender Ablauf festgelegt.

project source
  -> AST chunks or direct chunks
  -> official docs JSONL retrieval
  -> on-demand LLM verification
  -> score DB
  -> classified filesystem

Hier wird beschrieben, wie die Eingaben von Prompt und Quellcode an den AST‑Parser übergeben werden sollen.

Kernprinzipien

Prompt und Quellcode gehören zum selben Eingabebündel, haben jedoch unterschiedliche Rollen.

Eingabe Rolle
Benutzer‑Prompt Analyse‑Absicht, Fragetyp, gewünschte Ausgaberichtung
Quellcode Das eigentliche Zielobjekt, das der AST‑Parser parsen soll
Dateipfad/Projektinformationen Metadaten zur Identifizierung der Analyse‑Einheit und zur Klassifizierung nach Projekt

Der AST‑Parser verarbeitet den Quellcode. Der Prompt ist kein Zielobjekt für die AST‑Syntaxanalyse.

Der Prompt wird jedoch in nachfolgenden Schritten verwendet, um zu entscheiden, welche Teile zuerst betrachtet werden, welcher Retriever eingesetzt wird und welche Prüfungsanfrage an Qwen 3.6 gestellt wird.

Eingabepaket

Die Benutzereingaben werden zunächst zu einer einzigen Analyseanfrage zusammengefasst.

analysis_request
  prompt
  source
  source_kind
  project_id
  source_file
  source_sha256
  source_origin

Die Bedeutung jedes Elements ist wie folgt.

Eintrag Beschreibung
prompt Die vom Benutzer eingegebene Frage. Beispiel: “Was bedeutet dieser Code?”, “Braucht eine Konvertierung zu Godot 4?”
source Der ursprüngliche Quellcode-Text. So unverändert wie möglich erhalten.
source_kind gdscript, scene, resource, project_config, markdown usw., Eingabetypen
project_id ID zur Unterscheidung eines GitHub-Repos oder eines lokalen Projekts
source_file Dateipfad innerhalb des Projekts. Bei einem einzelnen Code-Einfügen wird ein temporärer Pfad verwendet.

| source_sha256 | Hash des ursprünglichen Quelltexts. Sowohl Einfügen als auch Dateieingabe werden zum Zeitpunkt der Anforderungserstellung berechnet. | | source_origin | github, local_directory, uploaded_file, pasted_snippet usw. Herkunft |

In diesem Schritt wird das Ausgabe‑JSONL‑Schema oder die Score‑DB‑Spalte nicht festgelegt. Ziel ist es, dass der AST‑Parser und die nachfolgenden Retriever/LLM‑Schritte dieselbe Eingabe anhand derselben Kriterien nachverfolgen können.

Einzelnes Quellcode‑Einfügen

Wenn der Benutzer Prompt und Quellcode direkt in die Web‑UI oder CLI eingibt.

Beispiel:

Muss das für Godot 4 konvertiert werden?
extends KinematicBody2D

func _physics_process(delta):
    move_and_slide()

Verarbeitungsablauf:

  1. Der Input wird in analysis_request eingeschlossen.
  2. source_origin wird als pasted_snippet festgelegt.
  3. Da es keinen tatsächlichen Dateipfad gibt, wird source_file mit einer temporären Kennung wie pasted://snippet-<id>.gd versehen.
  4. source_sha256 wird sofort anhand des eingefügten Originalcodes berechnet.
  5. source_kind wird anhand der Dateierweiterung, des Codeinhalts und der vom Benutzer angegebenen Sprache geschätzt.
  6. An den AST‑Parser werden source, source_kind, source_file und source_sha256 übergeben.
  7. Der Prompt wird nicht in den Parser gemischt, sondern in analysis_request.prompt gespeichert.

Projektverzeichnis‑Eingabe

Dies gilt für die Analyse eines GitHub‑Repos oder eines lokalen Ordners.

Verarbeitungsablauf:

  1. Das Projekt‑Root wird als project_id zusammengefasst.
  2. Das Dateisystem wird relativ zum Root‑Verzeichnis durchsucht.
  3. Für jede Datei wird zunächst festgehalten, ob sie einbezogen oder ausgeschlossen wird.
  4. Ausgeschlossene Dateien werden in excluded_files hinterlegt, damit sie in der Web‑UI oder im Log einsehbar sind.
  5. Eingeschlossene Dateien werden als Klartext‑Block mit einer Überschrift # <relative/path> ausgegeben.
  6. .gd‑Dateien werden an den AST‑Parser gesendet, um Funktionen/Code‑Snippets in Originalreihenfolge zu erzeugen.
  7. Für eingeschlossene Dateien, die nicht zum AST‑Parser gehören, werden direkte Fragmentierungsregeln angewendet, die zum Dateityp passen.
  8. Jedes Fragment wird mit Originalpfad, Originalreihenfolge und Hash an den Retriever übergeben.

Die anfänglichen Ziel‑Dateien entsprechen dem Referenz‑Roadmap.

Datei AST/Fragmentierungs‑Richtung
.gd GDScript‑AST, Funktionen, Klassen, Signale, Variablen, mögliche API‑Aufrufe
.tscn Szenen‑Node‑Typ, Skript‑Referenz, Ressourcen‑Referenz, exportierte Property
.tres Ressourcen‑Typ, Skript‑Klasse, Material/Shader/Ressourcen‑Hinweis
project.godot Godot‑Versions‑Hinweis, Autoload, Renderer, Feature, Input‑Map
README/Dokumentation Standardmäßig von der Quellcode‑AST‑Analyse ausgeschlossen. Der Ausschluss und dessen Grund werden in UI/Log vermerkt.

Projekt‑Quelltext‑Ausbreitung

Die Eingabe auf Projektebene wird zunächst als Sammlung von Klartext‑Blöcken pro Datei ausgegeben. Dieser Klartext ist nicht der finale Prompt, der komplett an das LLM übergeben wird, sondern eine Zwischen­darstellung, die Dateigrenzen und relative Pfade beibehält.

Grundform:

# project.godot
; original project.godot content

# scripts/player.gd
extends KinematicBody2D

func _physics_process(delta):
    move_and_slide(velocity)

# scenes/player.tscn
[gd_scene load_steps=2 format=3]
[node name="Player" type="CharacterBody2D"]

Regeln:

  • Die Dateikopfzeile hat die Form # <relative/path>.
  • Der relative Pfad bezieht sich auf das Projektwurzelverzeichnis.
  • Der Dateiinhalte sollte so weit wie möglich unverändert bleiben.
  • Dieser reine Textblock wird zur Überprüfung von Dateigrenzen und zum Debuggen verwendet.
  • In der eigentlichen Verarbeitung wird dieser gesamte Textblock nicht auf einmal an ein LLM oder einen AST‑Parser übergeben.
  • Jede durch einen Header getrennte Datei erhält zunächst eine Einschluss‑/Ausschluss‑Entscheidung.
  • Ausgeschlossene Dateien werden in excluded_files aufgeführt.
  • Eingeschlossene .gd‑Dateien durchlaufen eine analysis_request und werden dann an den AST‑Parser weitergeleitet.
  • Eingeschlossene Nicht‑.gd‑Text‑/Konfigurationsdateien werden direkt an den Chunker übergeben.

Auf diese Weise bleibt das vom Benutzer bereitgestellte „gesamte Projekt‑Quellcode“ in einer für Menschen lesbaren Form erhalten, während in nachfolgenden Schritten ausgeschlossene Dateien, AST‑Chunks, direkte Chunks und Retriever‑Payloads datei‑ bzw. chunkweise nachverfolgt werden können.

Überprüfungsplan für das Clone‑Projekt

Der Fluss vom AST‑Parser zum Retriever lässt sich nur anhand abstrakter Beispiele schwer nachvollziehen. Deshalb klonen wir ein echtes Godot‑Projekt und prüfen anhand des Dateibaums, der Textausbreitung, der Aufteilung in AST‑Chunk/Direct‑Chunk und des wiederholten Retriever‑Anfrage‑Flows.

Das Referenzprojekt wird wie folgt festgelegt.

Element Wert
repository godotengine/godot-demo-projects
Projektpfad 2d/dodge_the_creeps
Auswahlgrund Es ist ein offizielles Godot‑Demo, enthält .gd, .tscn, project.godot, README und Asset‑Referenzen, sodass der Eingabefluss des Projekts kompakt überprüft werden kann.
Speicherungsrichtlinie Der geklonte Quellcode wird nicht im Repository committet. Im Dokument bleiben nur der Baum und der Verarbeitungsplan.

Klonkriterien:

git clone --depth 1 --filter=blob:none --sparse https://github.com/godotengine/godot-demo-projects.git /tmp/idea_world_godot_demo_projects
cd /tmp/idea_world_godot_demo_projects
git sparse-checkout set 2d/dodge_the_creeps

Dieser Klon dient der Ablaufüberprüfung. Es werden keine Quellcodes oder Assets des geklonten Projekts im Repository kopiert.

Zu prüfende Objekte sind die vollständigen Dateipfade des geklonten Projekts. Zuerst werden alle Pfade in sortierter Reihenfolge ausgeklappt und jede Datei in Stücke aufgeteilt, die ein LLM lesen kann. Dabei wird keine separate Zwischen‑DB erstellt. Der Kern ist, dass nicht das gesamte Repository auf einmal gelesen wird, sondern Stücke in Pfad‑Reihenfolge erstellt und jedes Stück mehrfach aufgerufen wird.

2d/dodge_the_creeps
  .gitignore
  LICENSE
  README.md
  art/House In a Forest Loop.ogg
  art/House In a Forest Loop.ogg.import
  art/enemyFlyingAlt_1.png
  art/enemyFlyingAlt_1.png.import
  art/enemyFlyingAlt_2.png
  art/enemyFlyingAlt_2.png.import
  art/enemySwimming_1.png
  art/enemySwimming_1.png.import
  art/enemySwimming_2.png
  art/enemySwimming_2.png.import
  art/enemyWalking_1.png
  art/enemyWalking_1.png.import
  art/enemyWalking_2.png
  art/enemyWalking_2.png.import
  art/gameover.wav
  art/gameover.wav.import
  art/playerGrey_up1.png
  art/playerGrey_up1.png.import
  art/playerGrey_up2.png
  art/playerGrey_up2.png.import
  art/playerGrey_walk1.png
  art/playerGrey_walk1.png.import
  art/playerGrey_walk2.png
  art/playerGrey_walk2.png.import
  fonts/FONTLOG.txt
  fonts/LICENSE.txt
  fonts/Xolonium-Regular.ttf
  fonts/Xolonium-Regular.ttf.import
  hud.gd
  hud.gd.uid
  hud.tscn
  icon.webp
  icon.webp.import
  main.gd
  main.gd.uid
  main.tscn
  mob.gd
  mob.gd.uid
  mob.tscn
  player.gd
  player.gd.uid
  player.tscn
  project.godot
  screenshots/.gdignore
  screenshots/dodge.png

Dateifragmentierungsrichtlinien:

Dateityp Fragmentierungsart Aufrufmethode danach
.gd Der AST‑Parser erstellt Funktions‑, Signal‑, Variablen‑, Klassen‑Erweiterungs‑ und API‑Aufruf‑Fragmente in der Originalreihenfolge. Für jedes AST‑Fragment wird der Retriever aufgerufen und Prompt + AST‑Fragment + Suchergebnis an das LLM gesendet.
project.godot Wird nicht dem AST‑Parser zugeführt. Wenn es ein Einschlussziel ist, werden Abschnitt/Schlüssel‑Wert‑Paare, Hauptszene, Feature, Input‑Map‑Fragmente erstellt. Das Fragment wird direkt an den Retriever gesendet oder als Kontext um das AST‑Fragment der .gd‑Datei herum angehängt.
.tscn, .tres Wird nicht dem AST‑Parser zugeführt. Wenn es ein Einschlussziel ist, werden Node‑Block, ext_resource, sub_resource, Connection‑Fragmente erstellt. Das Fragment wird direkt an den Retriever gesendet oder als Kontext um das AST‑Fragment der zugehörigen .gd‑Datei herum angehängt.
.md, .txt, LICENSE Wird bei der Quellcode‑AST‑Analyse standardmäßig ausgeschlossen. Das Ausschluss‑ und Begründungs‑Protokoll wird in UI/Log vermerkt. Ohne separaten Dokumenten‑Analysemode wird es nicht an den Retriever gesendet.
.import, .uid, .gitignore, .gdignore Wird nicht dem AST‑Parser zugeführt. Wenn es ein Einschlussziel ist, werden Zeilen‑ oder Schlüssel‑Wert‑Fragmente erstellt. Das Fragment wird direkt an den Retriever gesendet oder nur als Kontext zur Überprüfung von Dateipfaden/Resource‑Beziehungen verwendet.
Bilder, Audio, Schriftarten usw. (Binärdateien) Nicht dem AST‑Parser zugeführt und die Original‑Bytes werden nicht an das LLM übergeben. Nur Pfad und Ausschlussgrund werden gespeichert. Der Pfad wird nur als Beziehungsinformation verwendet, wenn er in Text‑Fragmenten wie .tscn oder .import auftaucht.

Kurz gesagt: Als Eingabe für den AST‑Parser kommen nur .gd‑Dateien. Das gesamte Repository wird in Pfad‑Reihenfolge „ausgebreitet“, aber das Ergebnis ist kein einziger großer Text, der auf einmal an das LLM gesendet wird. Die Überschrift # <relative/path> dient dazu, nachzuverfolgen, aus welchem Pfad die Datei stammt und in welche Funktions‑/Code‑/Einstellungs‑Fragmente sie für den Retriever aufgeteilt wurde. Dateien, die wie .md ausgeschlossen werden, bleiben in der Ausschlussliste, während eingeschlossene .gd‑Dateien vom AST‑Parser sequenziell in Funktions‑ oder Code‑Fragment‑Einheiten zerlegt werden. Jeder LLM‑Aufruf erfolgt dann stets mehrmals als Prompt + aktuelles Fragment + Retriever‑Suchergebnis.

for each file in repository path order:
  record file path under "# <relative/path>"
  if file is excluded by source-analysis policy:
    save excluded_files entry with reason
    continue
  if file.path endswith ".gd":
    ast_chunks = ast_parse(full_file_text)
    for each ast_chunk in ast_chunks:
      retrieved = retrieve(build_query_from_chunk(prompt, ast_chunk))
      response = call_llm(prompt + ast_chunk + retrieved)
      validate(response)
      save_chunk_result(response)
  else:
    text_chunks = split_without_ast(file)
    for each text_chunk in text_chunks:
      retrieved = retrieve(build_query_from_chunk(prompt, text_chunk))
      response = call_llm(prompt + text_chunk + retrieved)
      validate(response)
      save_chunk_result(response)

Die endgültige LLM‑Eingabeeinheit hat immer die folgende Form.

llm_judgment_request
  user_prompt
  project_id
  source_file
  chunk_id
  chunk_order
  chunk_kind
  chunk_text
  retrieved_evidence

Retriever Such-Eingabe

Retriever hat nur die Aufgabe, den von mir bereitgestellten Code‑Abschnitt in der Datenbank zu suchen. Dateipfade, Chunk‑IDs und Chunk‑Reihenfolge sind keine Suchkriterien, sondern Nachverfolgungsinformationen. Deshalb werden in der Implementierung die beiden Objekte getrennt.

chunk_trace
  project_id
  source_file
  source_sha256
  chunk_id
  chunk_order
  chunk_kind
retriever_query
  query_text
  query_terms
  raw_chunk_text
  symbol_candidates
  api_call_candidates
  reference_candidates
  prompt_terms

chunk_trace wird nur für UI, Protokollierung, Neustart, Debugging und die Verknüpfung von LLM‑Antworten verwendet. An die Retriever‑Suchfunktion wird nur retriever_query übergeben.

AST/direct chunk
  -> build_retriever_query(chunk_text, extracted_candidates, user_prompt)
  -> retriever.search(retriever_query)
  -> orchestrator attaches chunk_trace to returned hits
  -> prompt + chunk_text + retrieved_hits -> LLM judgment

Das heißt, die Suche wird mit dem folgenden Wert durchgeführt.

Verwendet für die Suche Nicht für die Suche verwendet
chunk_text source_file
symbol_candidates chunk_id
api_call_candidates chunk_order
reference_candidates source_sha256
prompt_terms project_id

Dateipfade werden benötigt, nicht für die Suche, sondern für das Tracking. Zum Beispiel muss ein Trace wie player.gd:function:_process vorhanden sein, damit die Web‑UI anzeigen kann: „Diese Antwort stammt aus dem vierten Chunk von player.gd“. Wenn jedoch der Retriever in der Datenbank nach Belegen sucht, muss er nicht nach dem Pfad player.gd suchen, sondern nach dem Code innerhalb der _process‑Funktion und den API‑Kandidaten.

Zum Beispiel wird das Fragment der _process‑Funktion in player.gd zuerst in Tracking‑Informationen und Such‑Informationen aufgeteilt.

chunk_trace
  project_id: "github:godotengine/godot-demo-projects/2d/dodge_the_creeps"
  source_file: "player.gd"
  source_sha256: "87f4fcf7481dba031f74a363475cee75b81c3d42eb1347b318f6b824e37329a6"
  chunk_id: "player.gd:function:_process"
  chunk_order: 4
  chunk_kind: "function"
retriever_query
  raw_chunk_text: |
    func _process(delta):
    	var velocity = Vector2.ZERO # The player's movement vector.
    	if Input.is_action_pressed(&"move_right"):
    		velocity.x += 1
    	if Input.is_action_pressed(&"move_left"):
    		velocity.x -= 1
    	if Input.is_action_pressed(&"move_down"):
    		velocity.y += 1
    	if Input.is_action_pressed(&"move_up"):
    		velocity.y -= 1

    	if velocity.length() > 0:
    		velocity = velocity.normalized() * speed
    		$AnimatedSprite2D.play()
    	else:
    		$AnimatedSprite2D.stop()

    	position += velocity * delta
    	position = position.clamp(Vector2.ZERO, screen_size)

    	if velocity.x != 0:
    		$AnimatedSprite2D.animation = &"right"
    		$AnimatedSprite2D.flip_v = false
    		$Trail.rotation = 0
    		$AnimatedSprite2D.flip_h = velocity.x < 0
    	elif velocity.y != 0:
    		$AnimatedSprite2D.animation = &"up"
    		rotation = PI if velocity.y > 0 else 0
  symbol_candidates:
    - "Vector2"
    - "AnimatedSprite2D"
  api_call_candidates:
    - "Input.is_action_pressed"
    - "Vector2.ZERO"
    - "velocity.normalized"
    - "position.clamp"
  reference_candidates: []
  prompt_terms:
    - "Godot 3"
    - "Godot 4"
    - "migration"
  query_terms:
    - "Input.is_action_pressed"
    - "Vector2.ZERO"
    - "velocity.normalized"
    - "position.clamp"
    - "Vector2"
    - "AnimatedSprite2D"
    - "_process"
    - "Godot 3"
    - "Godot 4"
    - "migration"
  query_text: "Input.is_action_pressed Vector2.ZERO velocity.normalized position.clamp Vector2 AnimatedSprite2D _process Godot 3 Godot 4 migration"

Retriever Suchcode Richtlinie

Der folgende Code dient als Richtlinie, um die Implementierungsrichtung festzulegen. An den Retriever wird nur RetrieverQuery übergeben. ChunkTrace wird außerhalb des Retrievers vom Orchestrator gehalten und anschließend wieder mit den Suchergebnissen verknüpft.

from dataclasses import dataclass, field
import re


@dataclass(frozen=True)
class ChunkTrace:
    project_id: str
    source_file: str
    source_sha256: str
    chunk_id: str
    chunk_order: int
    chunk_kind: str


@dataclass(frozen=True)
class RetrieverQuery:
    query_text: str
    query_terms: list[str]
    raw_chunk_text: str
    symbol_candidates: list[str] = field(default_factory=list)
    api_call_candidates: list[str] = field(default_factory=list)
    reference_candidates: list[str] = field(default_factory=list)
    prompt_terms: list[str] = field(default_factory=list)


@dataclass(frozen=True)
class RetrievalHit:
    table_name: str
    record_id: str
    score: float
    title: str
    content: str
    metadata: dict


IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)?")


def unique_keep_order(values: list[str]) -> list[str]:
    seen: set[str] = set()
    result: list[str] = []
    for value in values:
        normalized = value.strip()
        if normalized and normalized not in seen:
            seen.add(normalized)
            result.append(normalized)
    return result


def extract_identifiers(chunk_text: str) -> list[str]:
    return unique_keep_order(IDENTIFIER_RE.findall(chunk_text))


def extract_prompt_terms(user_prompt: str) -> list[str]:
    terms: list[str] = []
    prompt = user_prompt.lower()
    if "godot 3" in prompt or "godot3" in prompt:
        terms.append("Godot 3")
    if "godot 4" in prompt or "godot4" in prompt:
        terms.append("Godot 4")
    if "변환" in user_prompt or "마이그레이션" in user_prompt or "migration" in prompt:
        terms.extend(["migration", "deprecated", "renamed"])
    if "설명" in user_prompt or "뜻" in user_prompt or "explain" in prompt:
        terms.extend(["description", "usage"])
    return terms


def build_retriever_query(
    *,
    user_prompt: str,
    chunk_text: str,
    symbol_candidates: list[str],
    api_call_candidates: list[str],
    reference_candidates: list[str],
) -> RetrieverQuery:
    prompt_terms = extract_prompt_terms(user_prompt)
    query_terms = unique_keep_order(
        api_call_candidates
        + symbol_candidates
        + reference_candidates
        + extract_identifiers(chunk_text)
        + prompt_terms
    )
    return RetrieverQuery(
        query_text=" ".join(query_terms),
        query_terms=query_terms,
        raw_chunk_text=chunk_text,
        symbol_candidates=symbol_candidates,
        api_call_candidates=api_call_candidates,
        reference_candidates=reference_candidates,
        prompt_terms=prompt_terms,
    )


def build_embedding_text(query: RetrieverQuery) -> str:
    return "\n".join(
        [
            query.raw_chunk_text,
            " ".join(query.api_call_candidates),
            " ".join(query.symbol_candidates),
            " ".join(query.reference_candidates),
            " ".join(query.prompt_terms),
        ]
    )


def retrieve_for_query(db, embedder, query: RetrieverQuery, limit_per_table: int = 5) -> list[RetrievalHit]:
    query_embedding = embedder.embed_query(build_embedding_text(query))
    tables = ["docs_chunks", "api_mapping", "label_prototypes"]
    hits: list[RetrievalHit] = []
    for table_name in tables:
        hits.extend(search_table(db, table_name, query, query_embedding, limit_per_table))
    hits.sort(key=lambda hit: hit.score, reverse=True)
    return hits


def search_table(db, table_name: str, query: RetrieverQuery, query_embedding: list[float], limit: int) -> list[RetrievalHit]:
    sql = """
    select
      %(table_name)s as table_name,
      id::text as record_id,
      title,
      content,
      metadata,
      1 - (embedding <=> %(query_embedding)s::vector) as vector_score,
      ts_rank(search_tsv, websearch_to_tsquery('simple', %(query_text)s)) as text_score,
      similarity(search_text, %(query_text)s) as trigram_score
    from godot_rag.dynamic_retrieval_view
    where table_name = %(table_name)s
      and (
        embedding is not null
        or
        search_tsv @@ websearch_to_tsquery('simple', %(query_text)s)
        or search_text %% %(query_text)s
      )
    order by
      embedding <=> %(query_embedding)s::vector asc,
      ts_rank(search_tsv, websearch_to_tsquery('simple', %(query_text)s)) desc,
      similarity(search_text, %(query_text)s) desc
    limit %(limit)s
    """
    rows = db.fetch_all(
        sql,
        {
            "table_name": table_name,
            "query_text": query.query_text,
            "query_embedding": query_embedding,
            "limit": limit,
        },
    )
    return [
        RetrievalHit(
            table_name=row["table_name"],
            record_id=row["record_id"],
            score=float(row["vector_score"] or 0) + float(row["text_score"] or 0) + float(row["trigram_score"] or 0),
            title=row["title"],
            content=row["content"],
            metadata={
                **row["metadata"],
                "query_text": query.query_text,
                "query_terms": query.query_terms,
            },
        )
        for row in rows
    ]


def retrieve_with_trace(db, embedder, trace: ChunkTrace, query: RetrieverQuery) -> dict:
    hits = retrieve_for_query(db, embedder, query)
    return {
        "trace": trace,
        "retriever_query": query,
        "hits": hits,
    }

Der wichtige Punkt im obigen Code ist folgender:

  • Die Retriever‑Suchfunktion nimmt nicht source_file, chunk_id und chunk_order als Suchkriterien entgegen.
  • Der Retriever führt eine Vektorsuche mit dem im Code‑Snippet erzeugten query_embedding durch, während query.query_text für die Keyword‑/Trigram‑Hilfssuche verwendet wird.
  • query_embedding wird aus raw_chunk_text, API‑Kandidaten, Symbol‑Kandidaten und den Prompt‑Intent‑Wörtern zusammengesetzt.
  • query.query_text wird aus den im Code‑Snippet innerhalb der Datei extrahierten APIs, Symbolen, Bezeichnern und Prompt‑Intent‑Wörtern erstellt.
  • Dateipfade und Chunk‑Reihenfolgen werden ausschließlich in ChunkTrace gespeichert.
  • retrieve_with_trace ist eine Orchestrator‑Funktion, die nach der Suche das Ergebnis mit dem Trace kombiniert. Die eigentliche Suche wird von retrieve_for_query durchgeführt.
  • docs_chunks, api_mapping und label_prototypes werden mit demselben RetrieverQuery abgefragt.

godot_rag.dynamic_retrieval_view ist keine neue Entscheidungslogik, sondern eine flachere Ansicht für die Suche. Auch wenn die Payload‑Strukturen der drei Tabellen unterschiedlich sind, sieht der Retriever für sie dieselben Spalten.

dynamic_retrieval_view
  table_name
  id
  title
  content
  embedding
  search_text
  search_tsv
  metadata

Die Suchziele werden wie folgt aufgeschlüsselt. Auch hier werden Quellfelder wie source_file, source_url als Metadaten beibehalten, jedoch nicht in die Standardsuchzeichenfolge aufgenommen. Die Suche sollte anhand von API‑/Symbol‑/Muster‑Code‑Fragments erfolgen.

Tabelle title content Werte für search_text
docs_chunks Dokumenttitel, Abschnittspfad, Symbol Dokumentinhalt‑Chunk doc_type, symbol, section_path, content, api_symbols
api_mapping Geänderte API oder vorheriger API‑Name Migrationsbeschreibung, Grund für die Änderung, Beispiel old_symbol, new_symbol, change_type, description, before_code, after_code
label_prototypes Prototype‑Name oder Label Verwendungsweise der Funktion, Parameterzusammensetzung, Beispiel für geändertes Aufrufmuster label, input_pattern, prompt_pattern, before_code, after_code, expected_response, explanation

Beispiel für eine SQL‑View‑Form:

create or replace view godot_rag.dynamic_retrieval_view as
select
  'docs_chunks' as table_name,
  id,
  coalesce(payload->>'title', payload->>'symbol', source_file) as title,
  coalesce(payload->>'content', '') as content,
  embedding,
  concat_ws(
    ' ',
    payload->>'doc_type',
    payload->>'symbol',
    payload->>'section_path',
    payload->>'content',
    payload->>'api_symbols'
  ) as search_text,
  search_tsv,
  payload || jsonb_build_object('source_file', source_file, 'source_url', payload->>'source_url') as metadata
from godot_rag.docs_chunks
union all
select
  'api_mapping' as table_name,
  id,
  concat_ws(' -> ', payload->>'old_symbol', payload->>'new_symbol') as title,
  concat_ws(' ', payload->>'description', payload->>'rationale') as content,
  embedding,
  concat_ws(
    ' ',
    payload->>'old_symbol',
    payload->>'new_symbol',
    payload->>'change_type',
    payload->>'description',
    payload->>'before_code',
    payload->>'after_code'
  ) as search_text,
  search_tsv,
  payload || jsonb_build_object('source_file', source_file, 'source_url', payload->>'source_url') as metadata
from godot_rag.api_mapping
union all
select
  'label_prototypes' as table_name,
  id,
  coalesce(payload->>'label', payload->>'name', source_file) as title,
  concat_ws(' ', payload->>'expected_response', payload->>'explanation') as content,
  embedding,
  concat_ws(
    ' ',
    payload->>'label',
    payload->>'input_pattern',
    payload->>'prompt_pattern',
    payload->>'before_code',
    payload->>'after_code',
    payload->>'expected_response',
    payload->>'explanation'
  ) as search_text,
  search_tsv,
  payload || jsonb_build_object('source_file', source_file) as metadata
from godot_rag.label_prototypes;

In dieser Ansicht ist der Wert, den der Retriever tatsächlich abruft, query_text. Zum Beispiel, wenn es sich um das _process‑Chunk handelt, wird wie folgt gesucht.

query_text =
  "Input.is_action_pressed Vector2.ZERO velocity.normalized position.clamp Vector2 AnimatedSprite2D _process Godot 3 Godot 4 migration"

Die Suchergebnisse sind keine sofort vertrauenswürdigen Antworten für das LLM, sondern Kandidatenbelege. Bei einer LLM‑Bewertungsanfrage sollten Chunk und Suchkandidaten zusammen übergeben werden, und Qwen 3.6 prüft erneut, „ob dieser Beleg mit dem aktuellen Chunk in Zusammenhang steht“.

Kriterien für die Übertragung‑Fehlersuche

Damit die KI nicht willkürlich nur „die Kernfunktionen“ aus einer .gd‑Datei auswählt und sendet oder umgekehrt die gesamte Datei als eine LLM‑Anfrage übermittelt, muss man vor und nach der Übertragung einen Diff prüfen können. Das zu prüfende Element ist nicht „ob die gesamte Originaldatei an das LLM gesendet wurde“, sondern „ob die aus der Originaldatei erzeugten Funktions‑/Code‑Abschnitte in ihrer ursprünglichen Reihenfolge vollständig nachverfolgt wurden und jeder Abschnitt als Retriever‑Anfrage weitergeleitet wurde“.

In der Phase der Dateiausbreitung wird zunächst ein Block des Originaltexts pro Pfad erstellt.

# player.gd
extends Area2D

signal hit

@export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window.

func _ready():
	screen_size = get_viewport_rect().size
	hide()


func _process(delta):
	var velocity = Vector2.ZERO # The player's movement vector.
	if Input.is_action_pressed(&"move_right"):
		velocity.x += 1
	if Input.is_action_pressed(&"move_left"):
		velocity.x -= 1
	if Input.is_action_pressed(&"move_down"):
		velocity.y += 1
	if Input.is_action_pressed(&"move_up"):
		velocity.y -= 1

	if velocity.length() > 0:
		velocity = velocity.normalized() * speed
		$AnimatedSprite2D.play()
	else:
		$AnimatedSprite2D.stop()

	position += velocity * delta
	position = position.clamp(Vector2.ZERO, screen_size)

	if velocity.x != 0:
		$AnimatedSprite2D.animation = &"right"
		$AnimatedSprite2D.flip_v = false
		$Trail.rotation = 0
		$AnimatedSprite2D.flip_h = velocity.x < 0
	elif velocity.y != 0:
		$AnimatedSprite2D.animation = &"up"
		rotation = PI if velocity.y > 0 else 0


func start(pos):
	position = pos
	rotation = 0
	show()
	$CollisionShape2D.disabled = false


func _on_body_entered(_body):
	hide() # Player disappears after being hit.
	hit.emit()
	# Must be deferred as we can't change physics properties on a physics callback.
	$CollisionShape2D.set_deferred(&"disabled", true)

Der nächste AST‑Parser hinterlässt eine Liste von Fragmenten.

ast_chunk_trace
  source_file: "player.gd"
  source_kind: "gdscript"
  source_sha256: "87f4fcf7481dba031f74a363475cee75b81c3d42eb1347b318f6b824e37329a6"
  chunks:
    - chunk_order: 1
      chunk_id: "player.gd:class_extends:1"
      node_kind: "class_extends"
      code_text: "extends Area2D"
    - chunk_order: 2
      chunk_id: "player.gd:signal:hit"
      node_kind: "signal"
      code_text: "signal hit"
    - chunk_order: 3
      chunk_id: "player.gd:function:_ready"
      node_kind: "function"
      code_text: |
        func _ready():
        	screen_size = get_viewport_rect().size
        	hide()
    - chunk_order: 4
      chunk_id: "player.gd:function:_process"
      node_kind: "function"
      code_text: |
        func _process(delta):
        	var velocity = Vector2.ZERO # The player's movement vector.
        	if Input.is_action_pressed(&"move_right"):
        		velocity.x += 1
        	if Input.is_action_pressed(&"move_left"):
        		velocity.x -= 1
        	if Input.is_action_pressed(&"move_down"):
        		velocity.y += 1
        	if Input.is_action_pressed(&"move_up"):
        		velocity.y -= 1

        	if velocity.length() > 0:
        		velocity = velocity.normalized() * speed
        		$AnimatedSprite2D.play()
        	else:
        		$AnimatedSprite2D.stop()

        	position += velocity * delta
        	position = position.clamp(Vector2.ZERO, screen_size)

        	if velocity.x != 0:
        		$AnimatedSprite2D.animation = &"right"
        		$AnimatedSprite2D.flip_v = false
        		$Trail.rotation = 0
        		$AnimatedSprite2D.flip_h = velocity.x < 0
        	elif velocity.y != 0:
        		$AnimatedSprite2D.animation = &"up"
        		rotation = PI if velocity.y > 0 else 0
    - chunk_order: 5
      chunk_id: "player.gd:function:start"
      node_kind: "function"
      code_text: |
        func start(pos):
        	position = pos
        	rotation = 0
        	show()
        	$CollisionShape2D.disabled = false
    - chunk_order: 6
      chunk_id: "player.gd:function:_on_body_entered"
      node_kind: "function"
      code_text: |
        func _on_body_entered(_body):
        	hide() # Player disappears after being hit.
        	hit.emit()
        	# Must be deferred as we can't change physics properties on a physics callback.
        	$CollisionShape2D.set_deferred(&"disabled", true)

Der Payload wird auch kurz vor dem Senden des Retrievers in Chunk‑Einheiten beibehalten.

retrieval_payload
  source_file: "player.gd"
  chunk_order: 4
  chunk_id: "player.gd:function:_process"
  chunk_text: |
    func _process(delta):
    	var velocity = Vector2.ZERO # The player's movement vector.
    	if Input.is_action_pressed(&"move_right"):
    		velocity.x += 1
    	if Input.is_action_pressed(&"move_left"):
    		velocity.x -= 1
    	if Input.is_action_pressed(&"move_down"):
    		velocity.y += 1
    	if Input.is_action_pressed(&"move_up"):
    		velocity.y -= 1

    	if velocity.length() > 0:
    		velocity = velocity.normalized() * speed
    		$AnimatedSprite2D.play()
    	else:
    		$AnimatedSprite2D.stop()

    	position += velocity * delta
    	position = position.clamp(Vector2.ZERO, screen_size)

    	if velocity.x != 0:
    		$AnimatedSprite2D.animation = &"right"
    		$AnimatedSprite2D.flip_v = false
    		$Trail.rotation = 0
    		$AnimatedSprite2D.flip_h = velocity.x < 0
    	elif velocity.y != 0:
    		$AnimatedSprite2D.animation = &"up"
    		rotation = PI if velocity.y > 0 else 0

Validierungsregeln:

expanded_source_block["player.gd"].sha256 == ast_chunk_trace.source_sha256
ast_chunk_trace.chunks[].chunk_order is strictly increasing
retrieval_payload.chunk_id exists in ast_chunk_trace.chunks[].chunk_id
retrieval_payload.chunk_order matches ast_chunk_trace.chunks[].chunk_order

Diese Validierung schlägt fehl, wird dann nicht gesendet. Diese Regel ist besonders wichtig in .gd‑Dateien. Die Absicht ist, nicht die gesamte .gd‑Datei in das LLM zu geben, sondern zu prüfen, ob das aus .gd stammende Funktions‑/Code‑Fragment seine ursprünglichen Pfade und Reihenfolge beibehält, wenn es zum Retriever weitergeleitet wird.

Wenn dieses Projekt als Eingabe erhalten wird, wird es zuerst in eine für Menschen lesbare Klartext‑Ansammlung ausgeklappt. Der untenstehende Block zeigt den gesamten Originaltext von Textdateien (ausgenommen Binär‑Assets) in Einheiten von # <relative/path>. Dabei sind auch Dateien wie .md, LICENSE enthalten, die bei der Quellcode‑Analyse als Ausschlusskandidaten gelten. Im Dokumentationsbeispiel wird der Inhalt innerhalb des relativen Pfads nicht teilweise weggelassen, um die Nachverfolgungsgrundlage zu prüfen. Wird jedoch eine Ausschluss‑Policy im Implementierungs‑Workflow angewendet, werden die ausgeschlossenen Dateien nicht an den Retriever gesendet, sondern zusammen mit dem Grund in excluded_files vermerkt.


# .gitignore
.import
logs/

# LICENSE
MIT License

Copyright (c) 2017 KidsCanCode

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

# README.md
# Dodge the Creeps

This is a simple game where your character must move
and avoid the enemies for as long as possible.

This is a finished version of the game featured in the
["Your first 2D game"](https://docs.godotengine.org/en/latest/getting_started/first_2d_game/index.html)
tutorial in the documentation. For more details,
consider following the tutorial in the documentation.

Language: GDScript

Renderer: Compatibility

> [!NOTE]
>
> There is a C# version available [here](https://github.com/godotengine/godot-demo-projects/tree/master/mono/dodge_the_creeps).

Check out this demo on the asset library: https://godotengine.org/asset-library/asset/2712

## Screenshots

![GIF from the documentation](https://docs.godotengine.org/en/latest/_images/dodge_preview.gif)

![Screenshot](/content/docs/de/roadmaps/screenshots/dodge.png)

## Copying

`art/House In a Forest Loop.ogg` Copyright &copy; 2012 [HorrorPen](https://opengameart.org/users/horrorpen), [CC-BY 3.0: Attribution](https://creativecommons.org/licenses/by/3.0/). Source: https://opengameart.org/content/loop-house-in-a-forest

Images are from "Abstract Platformer". Created in 2016 by kenney.nl, [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). Source: https://www.kenney.nl/assets/abstract-platformer

Font is "Xolonium". Copyright &copy; 2011-2016 Severin Meyer <sev.ch@web.de>, with Reserved Font Name Xolonium, SIL open font license version 1.1. Details are in `fonts/LICENSE.txt`.

# art/House In a Forest Loop.ogg.import
[remap]

importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://sgfduhhw4pno"
path="res://.godot/imported/House In a Forest Loop.ogg-1a6a72ae843ad792b7039931227e8d50.oggvorbisstr"

[deps]

source_file="res://art/House In a Forest Loop.ogg"
dest_files=["res://.godot/imported/House In a Forest Loop.ogg-1a6a72ae843ad792b7039931227e8d50.oggvorbisstr"]

[params]

loop=true
loop_offset=0.0
bpm=0.0
beat_count=0
bar_beats=4

# art/enemyFlyingAlt_1.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://dun74wipekpfq"
path="res://.godot/imported/enemyFlyingAlt_1.png-559f599b16c69b112c1b53f6332e9489.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/enemyFlyingAlt_1.png"
dest_files=["res://.godot/imported/enemyFlyingAlt_1.png-559f599b16c69b112c1b53f6332e9489.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/enemyFlyingAlt_2.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://vusf51hepduk"
path="res://.godot/imported/enemyFlyingAlt_2.png-31dc7310eda6e1b721224f3cd932c076.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/enemyFlyingAlt_2.png"
dest_files=["res://.godot/imported/enemyFlyingAlt_2.png-31dc7310eda6e1b721224f3cd932c076.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/enemySwimming_1.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://d182mv7y80xqy"
path="res://.godot/imported/enemySwimming_1.png-dd0e11759dc3d624c8a704f6e98a3d80.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/enemySwimming_1.png"
dest_files=["res://.godot/imported/enemySwimming_1.png-dd0e11759dc3d624c8a704f6e98a3d80.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/enemySwimming_2.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://dmgglhdyowipd"
path="res://.godot/imported/enemySwimming_2.png-4c0cbc0732264c4ea3290340bd4a0a62.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/enemySwimming_2.png"
dest_files=["res://.godot/imported/enemySwimming_2.png-4c0cbc0732264c4ea3290340bd4a0a62.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/enemyWalking_1.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://dgwhuvn7qb4iy"
path="res://.godot/imported/enemyWalking_1.png-5af6eedbe61b701677d490ffdc1e6471.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/enemyWalking_1.png"
dest_files=["res://.godot/imported/enemyWalking_1.png-5af6eedbe61b701677d490ffdc1e6471.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/enemyWalking_2.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://dyw702efe6meu"
path="res://.godot/imported/enemyWalking_2.png-67c480ed60c35e95f5acb0436246b935.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/enemyWalking_2.png"
dest_files=["res://.godot/imported/enemyWalking_2.png-67c480ed60c35e95f5acb0436246b935.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/gameover.wav.import
[remap]

importer="wav"
type="AudioStreamWAV"
uid="uid://td2mgko63p61"
path="res://.godot/imported/gameover.wav-98c95c744b35280048c2bd093cf8a356.sample"

[deps]

source_file="res://art/gameover.wav"
dest_files=["res://.godot/imported/gameover.wav-98c95c744b35280048c2bd093cf8a356.sample"]

[params]

force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=true
edit/normalize=true
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

# art/playerGrey_up1.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://bcow5c46vixno"
path="res://.godot/imported/playerGrey_up1.png-6bd114d0a6beac91f48e3a7314d44564.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/playerGrey_up1.png"
dest_files=["res://.godot/imported/playerGrey_up1.png-6bd114d0a6beac91f48e3a7314d44564.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/playerGrey_up2.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://dw3lwgwhpbfx8"
path="res://.godot/imported/playerGrey_up2.png-d6aba85f5f2675ebc7045efa7552ee79.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/playerGrey_up2.png"
dest_files=["res://.godot/imported/playerGrey_up2.png-d6aba85f5f2675ebc7045efa7552ee79.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/playerGrey_walk1.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://b2aofu01vxvea"
path="res://.godot/imported/playerGrey_walk1.png-c4773fe7a7bf85d7ab732eb4458c2742.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/playerGrey_walk1.png"
dest_files=["res://.godot/imported/playerGrey_walk1.png-c4773fe7a7bf85d7ab732eb4458c2742.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# art/playerGrey_walk2.png.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://ddjou2q6gxlfr"
path="res://.godot/imported/playerGrey_walk2.png-34d2d916366100182d08037c51884043.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://art/playerGrey_walk2.png"
dest_files=["res://.godot/imported/playerGrey_walk2.png-34d2d916366100182d08037c51884043.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# fonts/FONTLOG.txt
Please distribute this file along with the Xolonium fonts when possible.


Source

	Find the sourcefiles of Xolonium at
	<gitlab.com/sev/xolonium>


Credits

	Xolonium is created with FontForge <fontforge.org>,
	Inkscape <inkscape.org>, Python <python.org>, and
	FontTools <github.com/fonttools>.

	It originated as a custom font for the open-source
	game Xonotic <xonotic.org>. With many thanks to the
	Xonotic community for your support.


Supported OpenType features

	case  Provides case sensitive placement of punctuation,
	      brackets, and math symbols for uppercase text.
	frac  Replaces number/number sequences with diagonal fractions.
	      Numbers that touch a slash should not exceed 10 digits.
	kern  Provides kerning for Latin, Greek, and Cyrillic scripts.
	locl  Dutch: Replaces j with a stressed version if it follows í.
	      Sami: Replaces n-form Eng with the preferred N-form version.
	      Romanian and Moldovan: Replaces ŞşŢţ with the preferred ȘșȚț.
	pnum  Replaces monospaced digits with proportional versions.
	sinf  Replaces digits with scientific inferiors below the baseline.
	subs  Replaces digits with subscript versions on the baseline.
	sups  Replaces digits with superscript versions.
	zero  Replaces zero with a slashed version.


Supported glyph sets

	Adobe Latin 3
	OpenType W1G
	ISO 8859-1   Western European
	ISO 8859-2   Central European
	ISO 8859-3   South European
	ISO 8859-4   North European
	ISO 8859-5   Cyrillic
	ISO 8859-7   Greek
	ISO 8859-9   Turkish
	ISO 8859-10  Nordic
	ISO 8859-13  Baltic Rim
	ISO 8859-14  Celtic
	ISO 8859-15  Western European
	ISO 8859-16  South-Eastern European


Available glyphs

	 !"#$%&'()*+,-./0123456789:;<=>?
	@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
	`abcdefghijklmnopqrstuvwxyz{|}~

	 ¡¢£¤¥¦§¨©ª«¬ ®¯°±²³´µ¶·¸¹º»¼½¾¿
	ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß
	àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
	ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğ
	ĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľ
	ĿŀŁłŃńŅņŇňŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞş
	ŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽž
	ƒǺǻǼǽǾǿȘșȚțȷ

	ˆˇˉ˘˙˚˛˜˝

	ͺ;΄΅Ά·ΈΉΊΌΎΏΐ
	ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰ
	αβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ

	ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОП
	РСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп
	рстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџ
	ѢѣѲѳѴѵҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩ
	ҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽӀӁӂӇӈӋӌӏӐӑӒӓ
	ӔӕӖӗӘәӜӝӞӟӠӡӢӣӤӥӦӧӨөӮӯӰӱӲӳӴӵӶӷӸӹ
	Ԥԥ

	ḂḃḊḋḞḟṀṁṖṗṠṡṪṫẀẁẂẃẄẅẞỲỳ

	     ‒–—―‘’‚‛“”„‟†‡•…‰′″‹›‽‾⁄
	⁰⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎
	₤₦₩₫€₯₱₹₺₽₿
	℅ℓ№℗™Ω℮
	⅛⅜⅝⅞
	←↑→↓
	∂∆∏∑−∕∙√∞∟∫≈≠≤≥
	⌖
	■▬▮▰▲▶▼◀◆◊●◢◣◤◥
	☄★☠☢☣⚙⚛⚠⚡⛔
	❇❈❌❤❰❱❲❳
	fffiflffiffl
	🌌🌍🌎🌏👽💣🔥🔫
	😁😃😄😆😇😈😉😊😎😐😒😕😘
	😛😝😞😟😠😣😭😮😲😴😵
	🚀


Debugging glyphs

	  U+EFFD  Font version
	  U+F000  Font hinting indicator


Changelog

	Xolonium 4.1  2016-11-22  Severin Meyer  <sev.ch@web.de>
		Reverted frac OpenType feature to a more stable implementation

	Xolonium 4.0  2016-10-08  Severin Meyer  <sev.ch@web.de>
		Decreased width of most glyphs
		Thinner vertical stems in Xolonium-Regular
		Thicker horizontal stems in Xolonium-Bold
		Revised diagonal stems
		Lowered middle bars
		Revised diacritical bars
		Added glyphs:
			ӏẞ₿
			U+2007 U+2008 U+2009 U+200A U+202F
			U+EFFD U+F000
		Revised glyphs:
			$&,JKQRXkwxy~¢¤ßǻ˜ζκλμξφЖУжћѴѵ∕₱₺₦₩€ℓ№≈ffffiffl
			❤🌍🌎🌏😁😄😇😈😉😊😘😭😮😴🚀
		Removed uncommon glyphs:
			ʼnſʼҌҍҎҏҾҿӃӄӇӈӚӛӪӫӬӭ
			U+0312 U+0313 U+0326
		Simplified OpenType features pnum, zero, and case
		Removed OpenType feature dlig
		Revised vertical metrics
		Merged outlines of composite glyphs in otf version
		Added ttf version with custom outlines and instructions
		Added woff and woff2 version

	Xolonium 3.1  2015-06-10  Severin Meyer  <sev.ch@web.de>
		Added currency glyphs:
			₦₩₫₱₹₺₽
		Revised glyph:
			₯
		Relicensed public release under the SIL Open Font License 1.1

	Xolonium 3.0  2015-05-04  Severin Meyer  <sev.ch@web.de>
		Decreased width of glyphs
		Decreased descender height
		Increased height of super/subscript glyphs
		Revised width of dashes, underscore, and overscore
		Sharper bends with more circular proportions
		Decreased stroke thickness of mathematical glyphs
		Revised diacritical marks
		Revised diacritical bars
		Revised Cyrillic hooks
		Revised glyphs:
			GQRYjmuwßŊŒſƒǻfffiffiffl
			ΞΨΩδζιξπςστυφω
			ЉЄДЛУЭЯбдлэяєљђєћѢѣҨҩҼҽӃӄӘә
			#$&'()*,/69?@[]{}~¡£¤¥§©®¿
			‹›₤€₯ℓ№℗℮←↑→↓∂∏∑∞≈▰☄❈❰❱❲❳😝
		Raised vertical position of mathematical glyphs
		Unified advance width of numeral and monetary glyphs
		Unified advance width of mathematical glyphs
		Revised bearings
		Rewrote kern feature
		Bolder Xolonium-Bold with improved proportions
		Updated glyph names to conform to the AGLFN 1.7
		Revised hints and PS Private Dictionary
		Added glyphs:
			ӶӷԤԥ
		Added OpenType features:
			case frac liga locl pnum sinf subs sups zero

	Xolonium 2.4  2014-12-23  Severin Meyer  <sev.ch@web.de>
		Added dingbats:
			⛔💣🔥
		Revised size and design of emoticons
		Revised dingbats:
			⌖☄☠☣⚙⚛⚠⚡❇❈🌌🌍🌎🌏🔫
		Removed dingbat:
			💥

	Xolonium 2.3  2014-08-14  Severin Meyer  <sev.ch@web.de>
		Bugfixed ε and έ, thanks to bowzee for the feedback

	Xolonium 2.2  2014-03-01  Severin Meyer  <sev.ch@web.de>
		Added dingbats:
			⌖◆●❌💥
		Revised dingbats:
			•←↑→↓◊☄★☠☣⚙⚛⚠⚡❇❈❤🌌🌍🌎🌏👽🔫🚀
		Removed dingbats:
			♻✪💡📡🔋🔧🔭

	Xolonium 2.1  2013-10-20  Severin Meyer  <sev.ch@web.de>
		Added dingbats:
			←↑→↓❰❱❲❳■▬▮▰▲▶▼◀◢◣◤◥
			☄★☠☢☣♻⚙⚛⚠⚡✪❇❈❤
			🌌🌍🌎🌏👽💡📡🔋🔧🔫🔭🚀
			😁😃😄😆😇😈😉😊😎😐😒😕
			😘😛😝😞😟😠😣😭😮😲😴😵

	Xolonium 2.0.1  2013-07-12  Severin Meyer  <sev.ch@web.de>
		Reorganised and simplified files

	Xolonium 2.0  2012-08-11  Severin Meyer  <sev.ch@web.de>
		Revised bends
		Revised thickness of uppercase diagonal stems
		Revised diacritical marks
		Revised hints and PS Private Dictionary
		Revised glyphs:
			*1469@DPRly{}§©®¶ÐÞƒΘΞαεζνξνυЄЉЊ
			ЏБЗЛУЧЪЫЬЭЯбзлчъыьэяєљњџ•€∂∙√∞∫≠
		Completed glyph sets:
			Adobe Latin 3
			OpenType World Glyph Set 1 (W1G)
			Ghostscript Standard (ghostscript-fonts-std-8.11)
		Added OpenType kern feature
		Added Xolonium-Bold

	Xolonium 1.2  2011-02-12  Severin Meyer  <sev.ch@web.de>
		Revised glyphs:
			D·Ðı
		Completed glyph sets:
			ISO 8859-7 (Greek)
			Unicode Latin Extended-A block
		Added glyphs:
			†‡•…‰⁄™∂∑−√∞≠≤≥

	Xolonium 1.1  2011-01-17  Severin Meyer  <sev.ch@web.de>
		Revised placement of cedilla and ogonek in accented glyphs
		Revised glyphs:
			,;DKTjkvwxy¥§Ð˛€
		Completed glyph sets:
			ISO 8859-2  (Central European)
			ISO 8859-3  (South European, Esperanto)
			ISO 8859-4  (North European)
			ISO 8859-5  (Cyrillic)
			ISO 8859-9  (Turkish)
			ISO 8859-10 (Nordic)
			ISO 8859-13 (Baltic Rim)
			ISO 8859-14 (Celtic)
			ISO 8859-16 (South-Eastern European)
		Added glyphs:
			ȷʼ̒ ЀЍѐѝ‒–—‘’‚‛“”„‟‹›

	Xolonium 1.0  2011-01-04  Severin Meyer  <sev.ch@web.de>
		Completed glyph sets:
			ISO 8859-1  (Western European)
			ISO 8859-15 (Western European)
		Added glyphs:
			ĄĆĘŁŃŚŹŻąćęłńśźżıˆˇ˙˚˛˜

# fonts/LICENSE.txt
Copyright 2011-2016 Severin Meyer <sev.ch@web.de>,
with Reserved Font Name Xolonium.

This Font Software is licensed under the SIL Open Font License,
Version 1.1. This license is copied below, and is also available
with a FAQ at <http://scripts.sil.org/OFL>


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

# fonts/Xolonium-Regular.ttf.import
[remap]

importer="font_data_dynamic"
type="FontFile"
uid="uid://bgv586r20ps8e"
path="res://.godot/imported/Xolonium-Regular.ttf-bc2981e3069cff4c34dd7c8e2bb73fba.fontdata"

[deps]

source_file="res://fonts/Xolonium-Regular.ttf"
dest_files=["res://.godot/imported/Xolonium-Regular.ttf-bc2981e3069cff4c34dd7c8e2bb73fba.fontdata"]

[params]

Rendering=null
antialiasing=1
generate_mipmaps=false
disable_embedded_bitmaps=true
multichannel_signed_distance_field=false
msdf_pixel_range=8
msdf_size=48
allow_system_fallback=true
force_autohinter=false
modulate_color_glyphs=false
hinting=1
subpixel_positioning=4
keep_rounding_remainders=true
oversampling=0.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
preload=[]
language_support={}
script_support={}
opentype_features={}

# hud.gd
extends CanvasLayer

signal start_game

func show_message(text):
	$MessageLabel.text = text
	$MessageLabel.show()
	$MessageTimer.start()


func show_game_over():
	show_message("Game Over")
	await $MessageTimer.timeout
	$MessageLabel.text = "Dodge the\nCreeps"
	$MessageLabel.show()
	await get_tree().create_timer(1).timeout
	$StartButton.show()


func update_score(score):
	$ScoreLabel.text = str(score)


func _on_StartButton_pressed():
	$StartButton.hide()
	start_game.emit()


func _on_MessageTimer_timeout():
	$MessageLabel.hide()

# hud.gd.uid
uid://c1g57034r2c0

# hud.tscn
[gd_scene format=3 uid="uid://b0efehuavobda"]

[ext_resource type="Script" uid="uid://c1g57034r2c0" path="res://hud.gd" id="1"]
[ext_resource type="FontFile" uid="uid://bgv586r20ps8e" path="res://fonts/Xolonium-Regular.ttf" id="2_2jm3i"]

[sub_resource type="InputEventAction" id="InputEventAction_fopy7"]
action = &"start_game"

[sub_resource type="Shortcut" id="4"]
events = [SubResource("InputEventAction_fopy7")]

[node name="HUD" type="CanvasLayer" unique_id=126421993]
script = ExtResource("1")

[node name="ScoreLabel" type="Label" parent="." unique_id=1314826100]
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 78.0
grow_horizontal = 2
theme_override_fonts/font = ExtResource("2_2jm3i")
theme_override_font_sizes/font_size = 60
text = "0"
horizontal_alignment = 1

[node name="MessageLabel" type="Label" parent="." unique_id=1611528703]
anchors_preset = 14
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_top = -79.5
offset_bottom = 79.5
grow_horizontal = 2
grow_vertical = 2
theme_override_fonts/font = ExtResource("2_2jm3i")
theme_override_font_sizes/font_size = 60
text = "Dodge the
Creeps"
horizontal_alignment = 1

[node name="StartButton" type="Button" parent="." unique_id=1561548516]
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -90.0
offset_top = -200.0
offset_right = 90.0
offset_bottom = -100.0
grow_horizontal = 2
grow_vertical = 0
theme_override_fonts/font = ExtResource("2_2jm3i")
theme_override_font_sizes/font_size = 60
shortcut = SubResource("4")
text = "Start"

[node name="MessageTimer" type="Timer" parent="." unique_id=1675980570]
one_shot = true

[connection signal="pressed" from="StartButton" to="." method="_on_StartButton_pressed"]
[connection signal="timeout" from="MessageTimer" to="." method="_on_MessageTimer_timeout"]

# icon.webp.import
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://dfklrdtaun0xt"
path="res://.godot/imported/icon.webp-e94f9a68b0f625a567a797079e4d325f.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://icon.webp"
dest_files=["res://.godot/imported/icon.webp-e94f9a68b0f625a567a797079e4d325f.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

# main.gd
extends Node

@export var mob_scene: PackedScene
var score

func game_over():
	$ScoreTimer.stop()
	$MobTimer.stop()
	$HUD.show_game_over()
	$Music.stop()
	$DeathSound.play()


func new_game():
	get_tree().call_group(&"mobs", &"queue_free")
	score = 0
	$Player.start($StartPosition.position)
	$StartTimer.start()
	$HUD.update_score(score)
	$HUD.show_message("Get Ready")
	$Music.play()


func _on_MobTimer_timeout():
	# Create a new instance of the Mob scene.
	var mob = mob_scene.instantiate()

	# Choose a random location on Path2D.
	var mob_spawn_location = get_node(^"MobPath/MobSpawnLocation")
	mob_spawn_location.progress_ratio = randf()

	# Set the mob's position to a random location.
	mob.position = mob_spawn_location.position

	# Set the mob's direction perpendicular to the path direction.
	var direction = mob_spawn_location.rotation + PI / 2

	# Add some randomness to the direction.
	direction += randf_range(-PI / 4, PI / 4)
	mob.rotation = direction

	# Choose the velocity for the mob.
	var velocity = Vector2(randf_range(150.0, 250.0), 0.0)
	mob.linear_velocity = velocity.rotated(direction)

	# Spawn the mob by adding it to the Main scene.
	add_child(mob)


func _on_ScoreTimer_timeout():
	score += 1
	$HUD.update_score(score)


func _on_StartTimer_timeout():
	$MobTimer.start()
	$ScoreTimer.start()

# main.gd.uid
uid://c4wt6ace7hycd

# main.tscn
[gd_scene format=3 uid="uid://bggkaprn62fwm"]

[ext_resource type="Script" uid="uid://c4wt6ace7hycd" path="res://main.gd" id="1_0r6n5"]
[ext_resource type="PackedScene" uid="uid://cao351pllxqpa" path="res://mob.tscn" id="2_50pww"]
[ext_resource type="PackedScene" uid="uid://bwhlkliwp13p4" path="res://player.tscn" id="3_veqnc"]
[ext_resource type="PackedScene" uid="uid://b0efehuavobda" path="res://hud.tscn" id="4_0qnje"]
[ext_resource type="AudioStream" uid="uid://sgfduhhw4pno" path="res://art/House In a Forest Loop.ogg" id="5_55d8h"]
[ext_resource type="AudioStream" uid="uid://td2mgko63p61" path="res://art/gameover.wav" id="6_hp1r0"]

[sub_resource type="Curve2D" id="1"]
_data = {
"points": PackedVector2Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480, 0, 0, 0, 0, 0, 480, 720, 0, 0, 0, 0, 0, 720, 0, 0, 0, 0, 0, 0)
}
point_count = 5

[node name="Main" type="Node" unique_id=1975992027]
script = ExtResource("1_0r6n5")
mob_scene = ExtResource("2_50pww")

[node name="ColorRect" type="ColorRect" parent="." unique_id=569320965]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.219608, 0.372549, 0.380392, 1)

[node name="Player" parent="." unique_id=927660131 instance=ExtResource("3_veqnc")]

[node name="MobTimer" type="Timer" parent="." unique_id=228987391]
wait_time = 0.5

[node name="ScoreTimer" type="Timer" parent="." unique_id=451982858]

[node name="StartTimer" type="Timer" parent="." unique_id=238316384]
wait_time = 2.0
one_shot = true

[node name="StartPosition" type="Marker2D" parent="." unique_id=568887530]
position = Vector2(240, 450)

[node name="MobPath" type="Path2D" parent="." unique_id=694323229]
curve = SubResource("1")

[node name="MobSpawnLocation" type="PathFollow2D" parent="MobPath" unique_id=1080274542]

[node name="HUD" parent="." unique_id=1879130737 instance=ExtResource("4_0qnje")]

[node name="Music" type="AudioStreamPlayer" parent="." unique_id=267371681]
stream = ExtResource("5_55d8h")

[node name="DeathSound" type="AudioStreamPlayer" parent="." unique_id=1715684712]
stream = ExtResource("6_hp1r0")

[connection signal="hit" from="Player" to="." method="game_over"]
[connection signal="timeout" from="MobTimer" to="." method="_on_MobTimer_timeout"]
[connection signal="timeout" from="ScoreTimer" to="." method="_on_ScoreTimer_timeout"]
[connection signal="timeout" from="StartTimer" to="." method="_on_StartTimer_timeout"]
[connection signal="start_game" from="HUD" to="." method="new_game"]

# mob.gd
extends RigidBody2D

func _ready():
	var mob_types = Array($AnimatedSprite2D.sprite_frames.get_animation_names())
	$AnimatedSprite2D.animation = mob_types.pick_random()
	$AnimatedSprite2D.play()


func _on_VisibilityNotifier2D_screen_exited():
	queue_free()

# mob.gd.uid
uid://cypxpb8arjrqt

# mob.tscn
[gd_scene format=3 uid="uid://cao351pllxqpa"]

[ext_resource type="Script" uid="uid://cypxpb8arjrqt" path="res://mob.gd" id="1"]
[ext_resource type="Texture2D" uid="uid://dun74wipekpfq" path="res://art/enemyFlyingAlt_1.png" id="2"]
[ext_resource type="Texture2D" uid="uid://vusf51hepduk" path="res://art/enemyFlyingAlt_2.png" id="3"]
[ext_resource type="Texture2D" uid="uid://dgwhuvn7qb4iy" path="res://art/enemyWalking_1.png" id="4"]
[ext_resource type="Texture2D" uid="uid://dyw702efe6meu" path="res://art/enemyWalking_2.png" id="5"]
[ext_resource type="Texture2D" uid="uid://d182mv7y80xqy" path="res://art/enemySwimming_1.png" id="6"]
[ext_resource type="Texture2D" uid="uid://dmgglhdyowipd" path="res://art/enemySwimming_2.png" id="7"]

[sub_resource type="SpriteFrames" id="1"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("2")
}, {
"duration": 1.0,
"texture": ExtResource("3")
}],
"loop": true,
"name": &"fly",
"speed": 3.0
}, {
"frames": [{
"duration": 1.0,
"texture": ExtResource("6")
}, {
"duration": 1.0,
"texture": ExtResource("7")
}],
"loop": true,
"name": &"swim",
"speed": 4.0
}, {
"frames": [{
"duration": 1.0,
"texture": ExtResource("4")
}, {
"duration": 1.0,
"texture": ExtResource("5")
}],
"loop": true,
"name": &"walk",
"speed": 4.0
}]

[sub_resource type="CapsuleShape2D" id="2"]
radius = 37.0
height = 100.0

[node name="Mob" type="RigidBody2D" unique_id=371809901 groups=["mobs"]]
collision_mask = 0
gravity_scale = 0.0
script = ExtResource("1")

[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=1998522389]
scale = Vector2(0.75, 0.75)
sprite_frames = SubResource("1")
animation = &"walk"

[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1880423722]
rotation = 1.5708
shape = SubResource("2")

[node name="VisibleOnScreenNotifier2D" type="VisibleOnScreenNotifier2D" parent="." unique_id=959995349]

[connection signal="screen_exited" from="VisibleOnScreenNotifier2D" to="." method="_on_VisibilityNotifier2D_screen_exited"]

# player.gd
extends Area2D

signal hit

@export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window.

func _ready():
	screen_size = get_viewport_rect().size
	hide()


func _process(delta):
	var velocity = Vector2.ZERO # The player's movement vector.
	if Input.is_action_pressed(&"move_right"):
		velocity.x += 1
	if Input.is_action_pressed(&"move_left"):
		velocity.x -= 1
	if Input.is_action_pressed(&"move_down"):
		velocity.y += 1
	if Input.is_action_pressed(&"move_up"):
		velocity.y -= 1

	if velocity.length() > 0:
		velocity = velocity.normalized() * speed
		$AnimatedSprite2D.play()
	else:
		$AnimatedSprite2D.stop()

	position += velocity * delta
	position = position.clamp(Vector2.ZERO, screen_size)

	if velocity.x != 0:
		$AnimatedSprite2D.animation = &"right"
		$AnimatedSprite2D.flip_v = false
		$Trail.rotation = 0
		$AnimatedSprite2D.flip_h = velocity.x < 0
	elif velocity.y != 0:
		$AnimatedSprite2D.animation = &"up"
		rotation = PI if velocity.y > 0 else 0


func start(pos):
	position = pos
	rotation = 0
	show()
	$CollisionShape2D.disabled = false


func _on_body_entered(_body):
	hide() # Player disappears after being hit.
	hit.emit()
	# Must be deferred as we can't change physics properties on a physics callback.
	$CollisionShape2D.set_deferred(&"disabled", true)

# player.gd.uid
uid://6s0lxctks3qn

# player.tscn
[gd_scene format=3 uid="uid://bwhlkliwp13p4"]

[ext_resource type="Script" uid="uid://6s0lxctks3qn" path="res://player.gd" id="1"]
[ext_resource type="Texture2D" uid="uid://b2aofu01vxvea" path="res://art/playerGrey_walk1.png" id="2"]
[ext_resource type="Texture2D" uid="uid://ddjou2q6gxlfr" path="res://art/playerGrey_walk2.png" id="3"]
[ext_resource type="Texture2D" uid="uid://bcow5c46vixno" path="res://art/playerGrey_up1.png" id="4"]
[ext_resource type="Texture2D" uid="uid://dw3lwgwhpbfx8" path="res://art/playerGrey_up2.png" id="5"]

[sub_resource type="SpriteFrames" id="1"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("2")
}, {
"duration": 1.0,
"texture": ExtResource("3")
}],
"loop": true,
"name": &"right",
"speed": 5.0
}, {
"frames": [{
"duration": 1.0,
"texture": ExtResource("4")
}, {
"duration": 1.0,
"texture": ExtResource("5")
}],
"loop": true,
"name": &"up",
"speed": 5.0
}]

[sub_resource type="CapsuleShape2D" id="2"]
radius = 27.0
height = 68.0

[sub_resource type="Gradient" id="3"]
colors = PackedColorArray(1, 1, 1, 0.501961, 1, 1, 1, 0)

[sub_resource type="GradientTexture1D" id="4"]
gradient = SubResource("3")

[sub_resource type="Curve" id="5"]
_data = [Vector2(0.00501098, 0.5), 0.0, 0.0, 0, 0, Vector2(0.994989, 0.324), 0.0, 0.0, 0, 0]
point_count = 2

[sub_resource type="CurveTexture" id="6"]
curve = SubResource("5")

[sub_resource type="ParticleProcessMaterial" id="7"]
gravity = Vector3(0, 0, 0)
scale_curve = SubResource("6")
color_ramp = SubResource("4")

[node name="Player" type="Area2D" unique_id=2141725708]
z_index = 10
script = ExtResource("1")

[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=1437394421]
scale = Vector2(0.5, 0.5)
sprite_frames = SubResource("1")
animation = &"right"

[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1954506745]
shape = SubResource("2")

[node name="Trail" type="GPUParticles2D" parent="." unique_id=1747300857]
z_index = -1
amount = 10
texture = ExtResource("2")
speed_scale = 2.0
process_material = SubResource("7")

[connection signal="body_entered" from="." to="." method="_on_body_entered"]

# project.godot
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
;   [section] ; section goes between []
;   param=value ; assign values to parameters

config_version=5

[application]

config/name="Dodge the Creeps"
config/description="This is a simple game where your character must move
and avoid the enemies for as long as possible.

This is a finished version of the game featured in the 'Your first 2D game'
tutorial in the documentation. For more details, consider
following the tutorial in the documentation."
config/tags=PackedStringArray("2d", "demo", "official")
run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.6")
config/icon="res://icon.webp"

[display]

window/size/viewport_width=480
window/size/viewport_height=720
window/size/window_width_override=480
window/size/window_height_override=720
window/stretch/mode="canvas_items"

[input]

move_left={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null)
]
}
move_right={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null)
]
}
move_up={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null)
]
}
move_down={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null)
]
}
start_game={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}

[rendering]

renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"

# screenshots/.gdignore

Binary‑Assets werden nicht im Klartext‑Body des Originals gespeichert, aber sie müssen unbedingt im repository path order sowie in den Ausschluss‑/Referenzlisten verbleiben. Beispiel: Wenn main.tscn auf res://art/gameover.wav verweist, dann enthält das LLM‑Eingabe‑Objekt den Chunk, der aus main.tscn stammt, die Pfad‑Metadaten, die auf art/gameover.wav verweisen, das Ergebnis des Retriever‑Suchvorgangs und die Benutzer‑Prompt‑Anfrage – alles innerhalb desselben Anforderungsobjekts. Die eigentlichen Binär‑Bytes des Originals werden weder an den Retriever noch an das LLM gesendet.

Beispiel für die Übermittlung pro Datei

Wenn man aus dem Klartext‑Block # player.gd herausnimmt, entsteht daraus ein einzelnes analysis_request.

Überprüfe, ob dieses Projekt Godot 3 oder Godot 4 ist, und finde bei Bedarf die Umwandlungsgrundlage.

extends Area2D

signal hit

@export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window.

func _ready():
	screen_size = get_viewport_rect().size
	hide()


func _process(delta):
	var velocity = Vector2.ZERO # The player's movement vector.
	if Input.is_action_pressed(&"move_right"):
		velocity.x += 1
	if Input.is_action_pressed(&"move_left"):
		velocity.x -= 1
	if Input.is_action_pressed(&"move_down"):
		velocity.y += 1
	if Input.is_action_pressed(&"move_up"):
		velocity.y -= 1

	if velocity.length() > 0:
		velocity = velocity.normalized() * speed
		$AnimatedSprite2D.play()
	else:
		$AnimatedSprite2D.stop()

	position += velocity * delta
	position = position.clamp(Vector2.ZERO, screen_size)

	if velocity.x != 0:
		$AnimatedSprite2D.animation = &"right"
		$AnimatedSprite2D.flip_v = false
		$Trail.rotation = 0
		$AnimatedSprite2D.flip_h = velocity.x < 0
	elif velocity.y != 0:
		$AnimatedSprite2D.animation = &"up"
		rotation = PI if velocity.y > 0 else 0


func start(pos):
	position = pos
	rotation = 0
	show()
	$CollisionShape2D.disabled = false


func _on_body_entered(_body):
	hide() # Player disappears after being hit.
	hit.emit()
	# Must be deferred as we can't change physics properties on a physics callback.
	$CollisionShape2D.set_deferred(&"disabled", true)

Beim AST‑Parser werden nicht die gesamte Anfrage, sondern nur die für die Quellcodeanalyse erforderlichen Felder übergeben.

ast_parse_input
  project_id: "github:godotengine/godot-demo-projects/2d/dodge_the_creeps"
  source_file: "player.gd"
  source_kind: "gdscript"
  source_text: |
    extends Area2D
    
    signal hit
    
    @export var speed = 400 # How fast the player will move (pixels/sec).
    var screen_size # Size of the game window.
    
    func _ready():
    	screen_size = get_viewport_rect().size
    	hide()
    
    
    func _process(delta):
    	var velocity = Vector2.ZERO # The player's movement vector.
    	if Input.is_action_pressed(&"move_right"):
    		velocity.x += 1
    	if Input.is_action_pressed(&"move_left"):
    		velocity.x -= 1
    	if Input.is_action_pressed(&"move_down"):
    		velocity.y += 1
    	if Input.is_action_pressed(&"move_up"):
    		velocity.y -= 1
    
    	if velocity.length() > 0:
    		velocity = velocity.normalized() * speed
    		$AnimatedSprite2D.play()
    	else:
    		$AnimatedSprite2D.stop()
    
    	position += velocity * delta
    	position = position.clamp(Vector2.ZERO, screen_size)
    
    	if velocity.x != 0:
    		$AnimatedSprite2D.animation = &"right"
    		$AnimatedSprite2D.flip_v = false
    		$Trail.rotation = 0
    		$AnimatedSprite2D.flip_h = velocity.x < 0
    	elif velocity.y != 0:
    		$AnimatedSprite2D.animation = &"up"
    		rotation = PI if velocity.y > 0 else 0
    
    
    func start(pos):
    	position = pos
    	rotation = 0
    	show()
    	$CollisionShape2D.disabled = false
    
    
    func _on_body_entered(_body):
    	hide() # Player disappears after being hit.
    	hit.emit()
    	# Must be deferred as we can't change physics properties on a physics callback.
    	$CollisionShape2D.set_deferred(&"disabled", true)
  source_sha256: "87f4fcf7481dba031f74a363475cee75b81c3d42eb1347b318f6b824e37329a6"

Parser teilt die Datei in Stücke. Bis die eigentliche Implementierung feststeht, wird das nächste Level von Stücken erwartet.

ast_parse_output
  source_file: "player.gd"
  chunks:
    - node_kind: "class_extends"
      code_text: "extends Area2D"
      symbol_candidates: ["Area2D"]
    - node_kind: "signal"
      code_text: "signal hit"
      symbol_candidates: ["hit"]
    - node_kind: "export_variable"
      code_text: "@export var speed = 400"
      symbol_candidates: ["@export", "speed"]
    - node_kind: "function"
      code_text: |
        func _process(delta):
        	var velocity = Vector2.ZERO # The player's movement vector.
        	if Input.is_action_pressed(&"move_right"):
        		velocity.x += 1
        	if Input.is_action_pressed(&"move_left"):
        		velocity.x -= 1
        	if Input.is_action_pressed(&"move_down"):
        		velocity.y += 1
        	if Input.is_action_pressed(&"move_up"):
        		velocity.y -= 1

        	if velocity.length() > 0:
        		velocity = velocity.normalized() * speed
        		$AnimatedSprite2D.play()
        	else:
        		$AnimatedSprite2D.stop()

        	position += velocity * delta
        	position = position.clamp(Vector2.ZERO, screen_size)

        	if velocity.x != 0:
        		$AnimatedSprite2D.animation = &"right"
        		$AnimatedSprite2D.flip_v = false
        		$Trail.rotation = 0
        		$AnimatedSprite2D.flip_h = velocity.x < 0
        	elif velocity.y != 0:
        		$AnimatedSprite2D.animation = &"up"
        		rotation = PI if velocity.y > 0 else 0
      api_call_candidates: ["Input.is_action_pressed", "Vector2.ZERO", "position.clamp"]
    - node_kind: "function"
      code_text: |
        func _on_body_entered(_body):
        	hide() # Player disappears after being hit.
        	hit.emit()
        	# Must be deferred as we can't change physics properties on a physics callback.
        	$CollisionShape2D.set_deferred(&"disabled", true)
      api_call_candidates: ["hit.emit", "set_deferred"]

Jeder Chunk wird erneut an die Retriever‑Anfrage weitergeleitet.

retrieval_request
  project_id: "github:godotengine/godot-demo-projects/2d/dodge_the_creeps"
  source_file: "player.gd"
  chunk_id: "player.gd:function:_process"
  prompt_intent: "version_check_and_explain"
  query_terms:
    - "Input.is_action_pressed"
    - "Vector2.ZERO"
    - "position.clamp"
    - "@export"
  candidate_tables:
    - "docs_chunks"
    - "api_mapping"
    - "label_prototypes"

Dieser Ablauf ist nicht speziell für player.gd. Er unterscheidet sich lediglich je nach Dateityp in der Phase der Chunk-Erstellung. main.gd, mob.gd und hud.gd wiederholen den Zyklus analysis_request → ast_parse_input → ast_parse_output.chunks[] → retrieval_request. Dateien, die nicht vom AST‑Parser verarbeitet werden, wie project.godot oder main.tscn, wiederholen den Zyklus analysis_request → direct_chunk_output.chunks[] → retrieval_request.

Der wiederholte Übertragungsablauf, der in diesem Beispielprojekt zu beobachten ist, lautet wie folgt:

  1. Das Projekt‑Root wird als ein einziges project_id behandelt.
  2. Godot‑bezogene Textdateien werden relativ zum Projektverzeichnis gesammelt.
  3. Jede Datei wird als Klartext‑Block mit einem Header # <relative/path> dargestellt.
  4. Jeder Klartext‑Block wird erneut zu einem dateibasierten analysis_request zusammengefasst.
  5. Für jedes analysis_request wird der source_sha256 berechnet.
  6. Für .gd‑Dateien erzeugt der AST‑Parser ast_parse_output.chunks[].
  7. Für Dateien, die nicht vom AST‑Parser verarbeitet werden, erzeugt der direkte Chunk‑Generator direct_chunk_output.chunks[].
  8. Auf Basis jedes Chunks wird eine Retriever‑Anfrage erstellt.
  9. Die Retriever‑Ergebnisse werden mit dem jeweiligen Chunk kombiniert und wiederholt als LLM‑Bewertungsanfrage gesendet.
  10. Alle erforderlichen Datei‑/Chunk‑Anfragen müssen erfolgreich beantwortet werden, bevor das Projekt als Kandidat für die Gesamtbewertung eingereicht wird.

Kurz gesagt, ein Projekt wird in mehrere Anfragen wie oben beschrieben aufgeteilt.

dodge_the_creeps/project.godot
  -> analysis_request
  -> direct_chunk_output.chunks[]
  -> retrieval_request per chunk

dodge_the_creeps/main.gd
  -> analysis_request
  -> ast_parse_output.chunks[]
  -> retrieval_request per chunk

dodge_the_creeps/player.gd
  -> analysis_request
  -> ast_parse_output.chunks[]
  -> retrieval_request per chunk

dodge_the_creeps/player.tscn
  -> analysis_request
  -> direct_chunk_output.chunks[]
  -> retrieval_request per chunk

dodge_the_creeps/mob.gd
  -> analysis_request
  -> ast_parse_output.chunks[]
  -> retrieval_request per chunk

dodge_the_creeps/mob.tscn
  -> analysis_request
  -> direct_chunk_output.chunks[]
  -> retrieval_request per chunk

dodge_the_creeps/hud.gd
  -> analysis_request
  -> ast_parse_output.chunks[]
  -> retrieval_request per chunk

dodge_the_creeps/hud.tscn
  -> analysis_request
  -> direct_chunk_output.chunks[]
  -> retrieval_request per chunk

In diesem Fall wird das gesamte Projekt nicht sofort abgeschlossen, wenn eine Datei oder ein Chunk fehlschlägt. Fehlgeschlagene Anfragen bleiben als „pending“ oder zum erneuten Versuch vorgesehen, und nur die erfolgreich beantworteten Chunks werden akkumuliert. Die Bewertung auf Projektebene erfolgt erst, nachdem die erforderlichen Wiederholungsanfragen ausreichend beantwortet wurden.

Wiederholungsanforderungsablauf

Ein Projekt wird nicht durch eine einzige LLM‑Anfrage beurteilt.

Die Projekteingabe wird, wie folgt, in mehrere Anfragen aufgeteilt.

project
  -> file requests
  -> AST chunks or direct chunks
  -> retrieval requests
  -> LLM judgment requests
  -> project-level aggregation

Die Wiederholungseinheit jeder Phase ist wie folgt:

Phase Wiederholungseinheit
Datei-Scan Dateien im Projekt
AST-Parser eingeschlossene .gd-Dateien
Direkte Fragmentierung eingeschlossene Dateien, die nicht Ziel des AST-Parsers sind
Ausschlussprotokoll .md, Binärdateien usw., auszuschließende Dateien
Retriever AST-Chunk oder Direct-Chunk
Qwen 3.6 Bewertung AST/Direct-Chunk + Suchbegründung
Projektklassifizierung Gesamtergebnis der Chunk-Bewertung

Daher endet ein Projekt nicht einfach mit wahr/falsch, sondern mehrere Dateien und mehrere Fragmente im Projekt müssen jeweils korrekt verarbeitet werden. Die endgültige Projektbewertung kann erst erfolgen, wenn die Ergebnisse von Retriever/LLM/Validator aller erforderlichen Fragmente gesammelt wurden.

Da Qwen 3.6 on-demand betrieben wird, ist es sinnvoller, es mehrmals in Einheiten von AST-Chunk oder Direct-Chunk aufzurufen, anstatt mit einer einzigen großen Anfrage abzuschließen. Dabei werden bereits abgeschlossene Chunks wiederverwendet, und nur fehlgeschlagene oder unterbrochene Chunks werden erneut angefordert.

Daten, die an den AST-Parser übergeben werden

Der Prompt wird nicht direkt in den AST-Parser eingegeben.

Die Parser-Eingabe wird wie folgt minimiert:

ast_parse_input
  project_id
  source_file
  source_kind
  source_text
  source_sha256

So die Trennung wird aus folgenden Gründen vorgenommen:

  • Der gleiche Quellcode kann in mehreren Prompts erneut analysiert werden.
  • Auch wenn der Prompt sich ändert, kann das AST‑Parsing‑Ergebnis wiederverwendet werden.
  • Der Parser verhindert, dass die „Absicht der Frage“ zu einer abweichenden Interpretation der Quellstruktur führt.
  • Nur in der Retriever/LLM‑Phase kann der Prompt verwendet werden, um die Entscheidungsrichtung zu ändern.

AST‑Parser‑Ausgabe

Der Parser erstellt statt den gesamten Quellcode direkt an das LLM zu übergeben, eine nachverfolgbare Liste von Fragmenten.

ast_parse_output
  project_id
  source_file
  source_sha256
  parse_status
  chunks[]

Jeder Chunk enthält die folgenden Informationen.

chunk
  chunk_id
  source_file
  span
  node_kind
  symbol_candidates
  api_call_candidates
  code_text
  surrounding_context
Eintrag Beschreibung
chunk_id Projekt/Datei/Bereich basierte deterministische ID
span Zeilen-/Spalten‑ oder Byte‑Offset‑Bereich
node_kind function, class, call, signal, property, scene_node usw.
symbol_candidates Klassen‑, Typ‑ und Methodennamen‑Vorschläge
api_call_candidates Godot‑API‑Aufruf‑Vorschläge
code_text Der Original‑Quellcode dieses Abschnitts
surrounding_context Der für die Beurteilung notwendige umgebende Code. Nicht die gesamte Datei, sondern nur der relevante Ausschnitt.

Direktes Chunk‑Ausgabe

Dateien, die nicht Ziel des AST‑Parsers sind, erzeugen direct_chunk_output. Diese Ausgabe ist kein AST, sondern das Ergebnis der Aufteilung von Text‑/Einstellungs‑/Szenen‑Strukturen in ihrer ursprünglichen Reihenfolge.

direct_chunk_output
  project_id
  source_file
  source_sha256
  chunk_status
  chunks[]

Jeder Chunk enthält die folgenden Informationen.

direct_chunk
  chunk_id
  source_file
  chunk_order
  span
  chunk_kind
  chunk_text
  reference_candidates

Beispiel:

direct_chunk
  source_file: "main.tscn"
  chunk_order: 3
  chunk_kind: "ext_resource"
  chunk_text: "[ext_resource type=\"Script\" uid=\"uid://c4wt6ace7hycd\" path=\"res://main.gd\" id=\"1_0r6n5\"]"
  reference_candidates:
    - "res://main.gd"

Dateien, die wie .md ausgeschlossen werden, bleiben in excluded_files und es wird kein direct_chunk_output erstellt.

Kombination von Prompt und Chunk

Der Prompt wird nach dem Parser erneut kombiniert.

analysis_request.prompt
+ ast chunk oder direct chunk
+ project/file metadata
-> retrieval_request
Prompt‑Absicht Priorität Retriever
“Was bedeutet dieser Code?” docs_chunks
“Muss das in Godot 4 konvertiert werden?” api_mapping, label_prototypes
“Ist dieser Code für Godot 3 oder 4?” docs_chunks, api_mapping, label_prototypes – alle als Kandidaten

Auch in diesem Schritt werden keine speziellen Tabellen besonders behandelt. Je nach Art des Prompts und des AST‑Fragments wird die erforderliche offizielle Dokumentations‑JSONL‑Begründung gesucht.

Einheit, die an das LLM übergeben wird

Bei Qwen 3.6 wird das gesamte Original‑Projekt nicht auf einmal übergeben.

Die Aufruf‑Einheiten des LLM sind die folgenden Gruppen:

llm_judgment_request
  prompt
  project_id
  source_file
  chunk_id
  chunk_order
  chunk_kind
  chunk_text
  surrounding_context
  retrieved_evidence
  judgment_contract

Hierbei ist judgment_contract noch kein endgültiges JSONL‑Schema. In der aktuellen Phase handelt es sich um einen Anforderungs‑Vertrag, der dem LLM mitteilt, was beurteilt werden soll.

retrieved_evidence ist ebenfalls noch kein endgültiges Speicher‑Schema, sondern ein abstraktes Feld, das ein Bündel von Suchergebnissen bezeichnet. In einem späteren Speicher‑Schritt kann es je nach Art der Evidenz in docs_chunks‑Evidenz‑ID, api_mapping‑Evidenz‑ID, label_prototypes‑Evidenz‑ID usw. aufgeteilt werden. In diesem Dokument wird es ausschließlich im Sinne von „Bündel von JSONL‑Evidenzen aus gefundenen offiziellen Dokumenten“ verwendet.

Beispiel:

Zu beurteilen:
- Ob dieser Codeausschnitt die Godot 3‑API verwendet
- Ob er für Godot 4 gültig ist
- Ob es Migrationsbegründungen gibt
- Ob die gefundenen offiziellen Dokumentationsbelege tatsächlich mit dem Code zusammenhängen

Wiederverwendungsgrenzen

Selbst wenn derselbe Quellcode verwendet wird, kann die Bewertung durch Retriever/LLM unterschiedlich ausfallen, wenn die Prompt variiert.

Umgekehrt kann das Ergebnis des AST‑Parsers bei identischem Quellcode wiederverwendet werden.

Schritt Wiederverwendungs‑Kriterium
AST‑Parse source_sha256, source_kind, Parser‑Version
Chunk‑Erstellung source_sha256, Chunking‑Version
Retriever‑Suche Prompt‑Intention, Chunk‑Symbole, Retrieval‑Version
LLM‑Bewertung Prompt, Chunk, Evidenz, Modell, Prompt‑Version

Wenn diese Grenzen eingehalten werden, vermischen sich „Analyse der Quellstruktur“ und „Bewertung nach Frageintention“ nicht.

Unterbrechungs‑/Fortsetzungs‑Perspektive

Da Analyseanfragen lang werden können, muss jeder Schritt fortsetzbar sein.

Minimale Speichereinheiten:

  • Ob der Dateiscann abgeschlossen ist
  • AST‑Parse‑Status pro Datei
  • Chunk‑Erstellungs‑Status
  • Retriever‑Such‑Status pro Chunk
  • LLM‑Bewertungs‑Status pro Chunk
  • Ob der Validator bestanden wurde

Wenn RunPod oder die lokale Anwendung zwischendurch stoppt, wird nur der gerade verarbeitete Chunk wieder in den „Pending“-Zustand versetzt, während abgeschlossene Dateien und Chunks wiederverwendet werden.

Noch nicht festgelegt

In diesem Dokument werden die folgenden Punkte nicht endgültig festgelegt.

Die Abhängigkeitsreihenfolge wird jedoch dokumentiert.

Reihenfolge Element Grund Aktueller Verfolgungs‑Ort
1 AST‑Parser‑Implementierung Je nach verwendetem Parser ändern sich die Chunk‑Struktur und die Wiederherstellungs‑Methoden. AST Parser에 넘기는 데이터, AST Parser 출력 in diesem Dokument
2 Endgültiges JSONL‑Antwort‑Schema Es muss festgelegt werden, welches JSONL zur Validierung der LLM‑Bewertung verwendet wird, um den Validator anzuhängen. docs/roadmaps/2026-06-25-source-analysis-scoring-architecture.md
3 Score‑DB‑Spalten Nachdem JSONL‑Antwort und Retriever‑Beweis‑IDs organisiert sind, können die Speicher‑Spalten definiert werden. docs/retrospectives/2026-06-25-source-analysis-scoring.md
4 Dateisystem‑Klassifizierungs‑Label Nachdem klar ist, welche Bewertungsergebnisse in der Score‑DB landen, werden Projekt‑Labels festgelegt. docs/roadmaps/2026-06-25-source-analysis-scoring-architecture.md
5 SFT/DPO‑Erstellungs‑Regeln Sobald das klassifizierte Dateisystem aufgebaut ist, werden diese separat entworfen. docs/roadmaps/2026-06-25-source-analysis-scoring-architecture.md

Derzeit ist nur ein Punkt festgelegt.

Prompt und Quellcode werden zu einer Analyseanfrage zusammengefasst,  
im AST‑Parser werden nur Quellcode und Dateimetadaten übergeben,  
der Prompt wird nach dem Parser in der Retriever/LLM‑Entscheidungsphase verwendet.