idea_world_labDEV JOURNAL
Friday, June 26, 2026

Flow of Passing Source Code Input to AST Analysis

Date: June 26, 2026

Purpose

This document supplements the front part of docs/roadmaps/2026-06-25-source-analysis-scoring-architecture.md.

The standard roadmap has already organized the following flow.

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

Here, we organize how to pass the user's prompt and source code to the AST Parser.

Core Principles

The prompt and source code come in the same input bundle, but their roles differ.

Input Role
User prompt Analysis intent, question type, expected output direction
Source code The actual target that the AST Parser will parse
File path/project information Metadata for identifying analysis units and categorizing by project

The AST Parser parses the source code. The prompt is not a target for AST syntax analysis.

However, the prompt is used in later stages to decide which pieces to prioritize, which Retriever to use, and what verification request to create for Qwen 3.6.

Input Package

User input is first grouped into a single analysis request.

analysis_request
  prompt
  source
  source_kind
  project_id
  source_file
  source_sha256
  source_origin

The meaning of each item is as follows.

Item Description
prompt The question entered by the user. Example: “What does this code mean?”, “Do I need to convert it to Godot 4?”
source The original source code text. Preserve it as faithfully as possible.
source_kind The type of input, such as gdscript, scene, resource, project_config, markdown, unknown, etc.
project_id An ID used to distinguish a GitHub repository or a local project
source_file The file path within the project. If it is a single code paste, use a temporary path.
source_sha256 The hash of the original source text. Calculated at request creation time for both paste and file inputs.
source_origin The source, such as github, local_directory, uploaded_file, pasted_snippet, etc.

At this stage, the output JSONL schema or score DB columns are not finalized. The goal is to enable the AST Parser and subsequent Retriever/LLM stages to track the same input using the same criteria.

Single source code paste

This occurs when the user directly provides a prompt and source code via the web UI or CLI.

Example:

Do we need to convert this to Godot 4?
extends KinematicBody2D

func _physics_process(delta):
    move_and_slide()

Processing Flow:

  1. Wrap the input in analysis_request.
  2. Set source_origin to pasted_snippet.
  3. Since there is no actual file path, use a temporary identifier such as pasted://snippet-<id>.gd for source_file.
  4. Compute source_sha256 immediately based on the pasted original code.
  5. Estimate source_kind from the file‑extension hint, code content, and the language specified by the user.
  6. Pass source, source_kind, source_file, and source_sha256 to the AST Parser.
  7. Store the prompt in analysis_request.prompt without mixing it into the Parser.

Project Directory Input

This applies when analyzing a GitHub repo or a local folder.

Processing Flow:

  1. Group the project root as project_id.
  2. Scan the filesystem using relative paths.
  3. Record inclusion/exclusion status for each file first.
  4. Keep excluded files in excluded_files so they can be viewed in the web UI or logs.
  5. Expand included files as plain‑text blocks prefixed with a # <relative/path> header.
  6. Send .gd files to the AST Parser to create functions/code fragments in their original order.
  7. Apply direct fragment‑creation rules appropriate to the file type for files that are not AST Parser targets.
  8. Each fragment, along with its original path, original order, and hash, is passed to the Retriever request.

The initial target files are the same as the reference roadmap.

File AST/Fragmentation Direction
.gd GDScript AST, functions, classes, signals, variables, API call candidates
.tscn scene node type, script reference, resource reference, exported property
.tres resource type, script class, material/shader/resource clue
project.godot Godot version hint, autoload, renderer, feature, input map
README/documents By default excluded from source‑code AST analysis. Record exclusion status and reason in UI/logs.

Project Source Plain‑Text Expansion

Project‑level input is first expanded into plain‑text bundles per file. This plain text is not the final prompt to be fed to the LLM in one go; it is an intermediate representation that preserves file boundaries and relative paths.

Basic Form:

# 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"]

Rules:

  • File headers are in the form # <relative/path>.
  • Relative paths are based on the project root.
  • Keep the file body as close to the original text as possible.
  • This plain‑text block is used to verify file boundaries and for debugging.
  • In actual processing, the entire plain‑text block is not passed to an LLM or AST parser at once.
  • Each file separated by a header first undergoes an include/exclude decision.
  • Excluded files are listed in excluded_files.
  • Included .gd files go through an analysis_request before entering the AST parser.
  • Included non‑.gd text/config files are sent directly to the chunker.

By doing this, the “entire project source” provided by the user remains in a human‑readable form, while later stages can track excluded files, AST chunks, direct chunks, and Retriever payloads on a per‑file or per‑chunk basis.

Clone Project Verification Plan

The flow from the AST parser to the Retriever cannot be verified with abstract examples alone. Therefore, we will clone a real Godot project and use that project's file tree, plain‑text expansion, AST chunk/direct chunk separation, and Retriever request iteration as the verification basis.

The reference project is set up as follows.

Item Value
repository godotengine/godot-demo-projects
project path 2d/dodge_the_creeps
Reason for selection It is an official Godot demo and includes .gd, .tscn, project.godot, README, and asset references, making it easy to verify the project input flow on a small scale.
Storage policy The cloned source is not committed to the repository. The documentation retains only the tree and processing plan.

Clone criteria:

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

This clone is for flow verification. It does not copy the cloned project source or assets inside the repository.

The verification target is the full file paths of the cloned project. First, expand all paths in sorted order, and split each file into pieces that an LLM can read. No separate intermediate database is created here. The key is not “reading the entire repository at once” but “creating pieces in path order and invoking each piece multiple times.”

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

File‑by‑Fragmentation Criteria:

File Type Fragmentation Method Subsequent Call Method
.gd The AST Parser creates fragments for functions, signals, variables, class extensions, and API calls in the original order. For each AST fragment, call the Retriever and send prompt + AST fragment + search results to the LLM.
project.godot Not fed to the AST Parser. If included, create fragments for section/key‑value pairs, the main scene, features, and the input map. Send the fragment directly to the Retriever, or attach it as surrounding context for .gd AST judgments.
.tscn, .tres Not fed to the AST Parser. If included, create fragments for node blocks, ext_resource, sub_resource, and connections. Send the fragment directly to the Retriever, or attach it as surrounding context for the AST judgment of the linked .gd file.
.md, .txt, LICENSE Treated as default exclusion candidates in source‑code AST analysis. Record exclusion status and reason in the UI/log. If not in a separate document‑analysis mode, do not send to the Retriever.
.import, .uid, .gitignore, .gdignore Not fed to the AST Parser. If included, create line or key‑value fragments. Send the fragment directly to the Retriever, or use it only as surrounding context to verify file‑path/resource relationships.
Images, audio, fonts, and other binaries Not fed to the AST Parser, and the original bytes are not given to the LLM. Only the path and exclusion reason are recorded. Use the path only as relational information when it appears in text fragments such as .tscn or .import.

In other words, the only targets that go into the AST Parser are .gd files. The entire repository is unfolded in path order, but the result is not a single large body sent to the LLM at once. A # <relative/path> header marks where a file came from and tracks how it was later split into function/code/setting fragments that entered the Retriever. Files that are excluded, like .md, remain on the exclusion list, while included .gd files are broken down by the AST Parser into ordered function or code fragments. Subsequent LLM calls are always performed multiple times as prompt + current fragment + Retriever search results.

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)

The final LLM input unit is always in the following form.

llm_judgment_request
  user_prompt
  project_id
  source_file
  chunk_id
  chunk_order
  chunk_kind
  chunk_text
  retrieved_evidence

Retriever Search Input

The Retriever only serves to search the code snippets I provide in the DB. File path, chunk id, and chunk order are not search criteria but tracking information. Therefore, the implementation separates the two objects.

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 is used only for UI, logging, restart, debugging, and LLM response linking. Only retriever_query is passed to the Retriever search function.

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

Search should be performed with the following values.

Used for search Not used for search
chunk_text source_file
symbol_candidates chunk_id
api_call_candidates chunk_order
reference_candidates source_sha256
prompt_terms project_id

The reason a file path is needed is for tracing, not searching. For example, a trace like player.gd:function:_process must exist so that the web UI can show “this response came from the fourth chunk of player.gd.” However, when the Retriever looks for evidence in the DB, it should search by the code inside the _process function and API candidates, not by the path player.gd.

For instance, the _process function chunk of player.gd first separates the tracing information from the search information.

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 Search Code Standard

The code below serves as a standard to fix the implementation direction. Only RetrieverQuery is passed to the Retriever. ChunkTrace is held by the orchestrator outside the Retriever and is re‑associated with the search results.

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,
    }

The important points in the above code are as follows.

  • The Retriever search function does not take source_file, chunk_id, or chunk_order as search criteria.
  • The Retriever performs a vector search using the query_embedding created from the code snippet, and query.query_text is used for keyword/trigram auxiliary search.
  • query_embedding is made by combining raw_chunk_text, API candidates, symbol candidates, and prompt intent words.
  • query.query_text is created from API, symbol, identifier, and prompt intent words extracted from code snippets within the file.
  • File paths and chunk order are recorded only in ChunkTrace.
  • retrieve_with_trace is an orchestrator function that bundles the results and trace after the search. The actual search is performed by retrieve_for_query.
  • docs_chunks, api_mapping, and label_prototypes are searched with the same RetrieverQuery.

godot_rag.dynamic_retrieval_view is not a new decision logic but a flattened view for searching. Even if the payload structures of the three tables differ, it makes the Retriever see the same columns.

dynamic_retrieval_view
  table_name
  id
  title
  content
  embedding
  search_text
  search_tsv
  metadata

The search target is expanded as follows. Even here, source fields such as source_file and source_url are preserved as metadata, but they are not included in the default search string. The search should be performed using the API/symbol/pattern of the code snippet.

Table title content Values that go into search_text
docs_chunks document title, section path, symbol document body chunk doc_type, symbol, section_path, content, api_symbols
api_mapping changed API or previous API name migration description, reason for change, example old_symbol, new_symbol, change_type, description, before_code, after_code
label_prototypes prototype name or label function usage, argument composition, example of changed call pattern label, input_pattern, prompt_pattern, before_code, after_code, expected_response, explanation

Example SQL view format:

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;

On this view, the value that the Retriever actually searches for is query_text. For example, if it is a _process chunk, it searches as follows.

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

Search results are not direct answers to be trusted and fed to the LLM, but candidate evidence. For LLM judgment requests, include both the chunk and the search candidates, and let Qwen 3.6 re‑verify whether “this evidence is related to the current chunk.”

Transmission Debugging Criteria

To prevent the AI from arbitrarily selecting and sending only “core functions” from a .gd file, or conversely sending the entire file as a single LLM request, it must be possible to check the before‑and‑after state with a diff. The verification target is not “whether the whole original file went to the LLM,” but “whether the function/code fragments created from the original file are tracked completely in their original order, and each fragment was passed to a Retriever request.”

In the file expansion stage, first create original blocks by path.

# 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)

The next step leaves a list of fragments created by the AST Parser.

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)

Retriever payload just before sending is also left in chunk units.

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

Verification Rules:

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

If this verification fails, it will not be sent. This rule is especially important for .gd files. The intention is not to feed the entire .gd file into the LLM, but to ensure that a function/code snippet extracted from a .gd file reaches the Retriever without losing its original path and original order.

When this project is received as input, it is first expanded into a collection of plain‑text blocks that a human can verify. The block below shows the full original text of all text files, except binary assets, displayed in # <relative/path> units. This includes files such as .md and LICENSE that are candidates for exclusion in source‑code analysis. In the document example, the contents inside the relative paths are left intact without omission to verify the tracking criteria. However, if an exclusion policy is applied in the implementation, the excluded files are not sent to the Retriever and are listed in excluded_files together with the reason.


# .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/en/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 do not include the original bytes in the plain‑text body, but they must be retained in the repository path order and the exclusion/reference lists. For example, if main.tscn points to res://art/gameover.wav, the LLM inference input will contain, within the same request object, the chunk derived from main.tscn, the path metadata referencing art/gameover.wav, the Retriever search results, and the user prompt. The binary original bytes themselves are not sent to the Retriever or the LLM.

Example of File‑Level Transmission

If you extract the # player.gd block from a plain‑text bundle, it becomes a single analysis_request as follows.

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)

In the AST Parser, only the fields required for source analysis are passed, not the entire request.

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"

The parser splits the file into fragments. Until the actual implementation is finalized, expect fragments at the next level.

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"]

Each chunk is passed again to the Retriever request.

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"

This flow is not a special handling unique to player.gd. However, the chunk creation stage differs depending on the file type. main.gd, mob.gd, hud.gd repeat analysis_request -> ast_parse_input -> ast_parse_output.chunks[] -> retrieval_request. Files that are not targets of the AST Parser, such as project.godot and main.tscn, repeat analysis_request -> direct_chunk_output.chunks[] -> retrieval_request.

The repeat transmission flow to be verified in this example project is as follows.

  1. Treat the project root as a single project_id.
  2. Collect Godot‑related text files based on relative paths.
  3. Expand each file into a plain‑text block prefixed with a # <relative/path> header.
  4. Convert each plain‑text block back into a file‑level analysis_request.
  5. Compute source_sha256 for each analysis_request.
  6. .gd files have the AST Parser generate ast_parse_output.chunks[].
  7. Included files that are not AST Parser targets are chunked directly by the chunker, producing direct_chunk_output.chunks[].
  8. Create Retriever requests based on each chunk.
  9. Pair the Retriever results with the chunk and repeat LLM judgment requests.
  10. All required file/chunk requests must receive normal responses before the project‑level judgment candidate is submitted.

In other words, a single project is split into multiple requests as shown below.

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

At this point, even if a single file or chunk fails, the entire project is not immediately finalized. Failed requests remain as pending or retry targets, and only chunks that receive a normal response are accumulated. Project‑level judgment is performed only after the necessary repeated requests have been sufficiently answered.

Repeated Request Flow

A project is not judged based on a single LLM request.

Project input is divided into multiple requests as follows.

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

Each step’s repetition unit is as follows.

Step Repetition Unit
File Scan Files within the project
AST Parser Included .gd files
Direct Chunking Included files that are not targets of the AST Parser
Exclusion Log Files to be excluded such as .md, binaries, etc.
Retriever AST chunk or direct chunk
Qwen 3.6 Decision AST/direct chunk + retrieval evidence
Project Classification Overall result of chunk decisions

Therefore, a project does not end with a simple true/false; multiple files and multiple chunks inside the project must each be processed correctly. The final project judgment can be made only after all required chunk Retriever/LLM/Validator results have been gathered.

Since Qwen 3.6 runs on demand, it is better to invoke it multiple times on AST chunks or direct chunks rather than trying to finish with a single large request. At that point, reuse chunks that have already been completed, and only re‑request chunks that failed or were interrupted.

Data Passed to the AST Parser

Do not put the prompt directly into the AST Parser.

Minimize the parser input as shown below.

ast_parse_input
  project_id
  source_file
  source_kind
  source_text
  source_sha256

The reason for separating it this way is as follows.

  • The same source code can be re‑analyzed with multiple prompts.
  • Even if the prompt changes, the AST parsing result can be reused.
  • The parser prevents the source structure from being interpreted differently because of the “question intent.”
  • Only in the later Retriever/LLM stage can the prompt be used to change the direction of judgment.

AST Parser Output

Instead of passing the entire source directly to the LLM, the parser creates a list of traceable fragments.

ast_parse_output
  project_id
  source_file
  source_sha256
  parse_status
  chunks[]

Each chunk has the following information.

chunk
  chunk_id
  source_file
  span
  node_kind
  symbol_candidates
  api_call_candidates
  code_text
  surrounding_context
Item Description
chunk_id Deterministic ID based on project/file/scope
span line/column or byte offset range
node_kind function, class, call, signal, property, scene_node, etc.
symbol_candidates Candidate class names, type names, method names
api_call_candidates Candidate Godot API calls
code_text Original code of the fragment
surrounding_context Surrounding code needed for judgment. Only the necessary range is provided, not the entire file.

Direct Chunk Output

Files that are not targets of the AST Parser create a direct_chunk_output. This output is not an AST but the result of splitting the original text/settings/scene structure in order.

direct_chunk_output
  project_id
  source_file
  source_sha256
  chunk_status
  chunks[]

Each chunk has the following information.

direct_chunk
  chunk_id
  source_file
  chunk_order
  span
  chunk_kind
  chunk_text
  reference_candidates

Example:

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"

Files excluded like .md are left in excluded_files without creating direct_chunk_output.

Prompt and Chunk Combination

The prompt is recombined after the Parser.

analysis_request.prompt
+ ast chunk or direct chunk
+ project/file metadata
-> retrieval_request
Prompt Intent Priority Retriever
“What does this code mean?” docs_chunks
“Do I need to convert to Godot 4?” api_mapping, label_prototypes
“Is this code for Godot 3 or 4?” docs_chunks, api_mapping, label_prototypes all candidates

Even at this stage, no specific tables are given special treatment. Depending on the nature of the prompt and the AST fragment, we find the necessary official documentation JSONL evidence.

Unit Passed to the LLM

The entire original project is not passed to Qwen 3.6 at once.

The LLM call units are the following bundles.

llm_judgment_request
  prompt
  project_id
  source_file
  chunk_id
  chunk_order
  chunk_kind
  chunk_text
  surrounding_context
  retrieved_evidence
  judgment_contract

Here, judgment_contract is not yet the final JSONL schema. At this stage, it is a request contract that tells the LLM what it should judge.

retrieved_evidence is not the final storage schema either; it is an abstract field referring to a bundle of search results. In the later storage stage, it can be split according to the type of evidence, such as docs_chunks evidence ID, api_mapping evidence ID, or label_prototypes evidence ID. In this document, it is used only with the meaning “bundle of retrieved official document JSONL evidence.”

Example:

Things to consider:
- Whether this code snippet uses the Godot 3 API
- Whether it is valid for Godot 4
- Whether there is a migration basis
- Whether the referenced official documentation actually relates to the code

Reuse Boundary

Even with the same source code, the Retriever/LLM judgment can differ if the prompt is different.

Conversely, if the source code is the same, the AST Parser results can be reused.

Stage Reuse Criteria
AST parse source_sha256, source_kind, Parser version
chunk generation source_sha256, chunking version
Retriever search prompt intent, chunk symbols, retrieval version
LLM judgment prompt, chunk, evidence, model, prompt version

If this boundary is respected, “source structure analysis” and “judgment based on question intent” do not get mixed.

Pause/Resume Perspective

Because analysis requests can become lengthy, each stage must be resumable.

Minimum storage units:

  • Whether file scan is completed
  • AST parse state per file
  • Chunk generation state
  • Retriever search state per chunk
  • LLM judgment state per chunk
  • Whether Validator passed

If RunPod or a local app stops midway, only the chunk that was being processed is returned to pending, while completed files and chunks are reused.

Items Not Yet Determined

This document does not finalize the following.

The dependency order is recorded, however.

Order Item Reason Current Tracking Location
1 AST Parser implementation The chunk structure and failure recovery method vary depending on which parser is used. AST Parser에 넘기는 데이터, AST Parser 출력 in this document
2 Final JSONL response schema We need to decide which JSONL to validate the LLM judgment results against before attaching a Validator. docs/roadmaps/2026-06-25-source-analysis-scoring-architecture.md
3 score DB columns After the JSONL response and Retriever evidence IDs are organized, we can determine the storage columns. docs/retrospectives/2026-06-25-source-analysis-scoring.md
4 Filesystem classification label After seeing what judgment results accumulate in the score DB, we will set the project label. docs/roadmaps/2026-06-25-source-analysis-scoring-architecture.md
5 SFT/DPO generation rules Once the classified filesystem is accumulated, it will be designed separately. docs/roadmaps/2026-06-25-source-analysis-scoring-architecture.md

Only one thing is currently finalized.

Prompt and source code are bundled into a single analysis request,  
Only the source code and file metadata are passed to the AST Parser,  
The prompt is used in the Retriever/LLM decision stage after the Parser.