diff --git a/.slop_cache/summary_cache.json b/.slop_cache/summary_cache.json index 8e3c88e..9d544c2 100644 --- a/.slop_cache/summary_cache.json +++ b/.slop_cache/summary_cache.json @@ -246,5 +246,37 @@ "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1072\\test_auto_aggregate_skip0\\file1.txt": { "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", "summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" + }, + "C:\\projects\\manual_slop\\tests\\test_minimax_provider.py": { + "hash": "107ae6075993f4bac175c08dec0627b1c4f3ee187d516acdf48d7ee179cbd5e3", + "summary": "This file contains unit tests for the `minimax` AI provider integration within the `ai_client` module. It verifies the correct selection of models, default model behavior, listing of available models, and history bleed statistics.\n\n* Tests model selection and default model assignment.\n* Verifies the listing of supported Minimax models.\n* Checks history bleed statistics and provider integration.\n* Confirms Minimax provider is recognized in system lists.\n\n**Outline:**\n**Python** \u2014 41 lines\nimports: src, unittest\nfunctions: test_minimax_model_selection, test_minimax_default_model, test_minimax_list_models, test_minimax_history_bleed_stats, test_minimax_in_providers_list, test_minimax_in_app_controller_providers, test_minimax_credentials_template" + }, + "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1079\\test_auto_aggregate_skip0\\file1.txt": { + "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", + "summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose is to present this specific content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" + }, + "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1087\\test_auto_aggregate_skip0\\file1.txt": { + "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", + "summary": "This document contains a single line of text, \"content1\". Its purpose is to present this specific content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" + }, + "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1095\\test_auto_aggregate_skip0\\file1.txt": { + "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", + "summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" + }, + "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1096\\test_auto_aggregate_skip0\\file1.txt": { + "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", + "summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" + }, + "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1097\\test_auto_aggregate_skip0\\file1.txt": { + "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", + "summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" + }, + "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1099\\test_auto_aggregate_skip0\\file1.txt": { + "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", + "summary": "This document contains a single line of text, \"content1\". Its purpose and key takeaways are limited to this singular piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" + }, + "C:\\Users\\Ed\\AppData\\Local\\Temp\\pytest-of-Ed\\pytest-1100\\test_auto_aggregate_skip0\\file1.txt": { + "hash": "d0b425e00e15a0d36b9b361f02bab63563aed6cb4665083905386c55d5b679fa", + "summary": "This document, `file1.txt`, contains a single line of text: \"content1\". Its purpose appears to be to hold this specific piece of content.\n\n**Outline:**\n**TXT** \u2014 1 lines\npreview:\n```\ncontent1\n```" } } \ No newline at end of file diff --git a/conductor/tracks.md b/conductor/tracks.md index 720e881..c62220f 100644 --- a/conductor/tracks.md +++ b/conductor/tracks.md @@ -34,7 +34,7 @@ This file tracks all major tracks for the project. Each track has its own detail *Link: [./tracks/interactive_ast_tree_masking_20260510/](./tracks/interactive_ast_tree_masking_20260510/)* *Goal: Inspect C/C++ ASTs in the GUI and mask individual classes/functions as Def, Sig, or Hide.* -7. [ ] **Track: Phase 6 Review and Regression Verification** +7. [~] **Track: Phase 6 Review and Regression Verification** *Link: [./tracks/phase6_review_20260510/](./tracks/phase6_review_20260510/)* *Goal: Review Phase 6 implementation, perform full-suite batch regression testing, and expand test coverage for new context curation features.* diff --git a/conductor/tracks/phase6_review_20260510/plan.md b/conductor/tracks/phase6_review_20260510/plan.md index d80dddd..427dcc1 100644 --- a/conductor/tracks/phase6_review_20260510/plan.md +++ b/conductor/tracks/phase6_review_20260510/plan.md @@ -1,13 +1,13 @@ # Implementation Plan: Phase 6 Review and Regression Verification ## Phase 1: Regression Testing (Batch Execution) -- [ ] Task: Execute the full test suite in batches of 4 test files. -- [ ] Task: Document any failures and categorize by root cause. -- [ ] Task: Fix regressions introduced by Phase 6 (if any). -- [ ] Task: Conductor - User Manual Verification 'Phase 1' (Protocol in workflow.md) +- [x] Task: Execute the full test suite in batches of 4 test files. +- [x] Task: Document any failures and categorize by root cause. (Minimax regression, Snapshot crash). +- [x] Task: Fix regressions introduced by Phase 6 (if any). +- [x] Task: Conductor - User Manual Verification 'Phase 1' (Protocol in workflow.md) ## Phase 2: Feature Coverage (Core Logic) -- [ ] Task: Write unit tests for C++ AST Tree Masking logic using samples from `gencpp/base/components`. +- [~] Task: Write unit tests for C++ AST Tree Masking logic using samples from `gencpp/base/components`. - [ ] Task: Write unit tests for 'Fuzzy Anchor' resolution across simulated file edits. - [ ] Task: Write unit tests for `HistoryManager` context snapshot roundtrips. - [ ] Task: Conductor - User Manual Verification 'Phase 2' (Protocol in workflow.md) diff --git a/debug_line_296.py b/debug_line_296.py new file mode 100644 index 0000000..3eb12c6 --- /dev/null +++ b/debug_line_296.py @@ -0,0 +1,13 @@ +from src.file_cache import ASTParser +from pathlib import Path +parser = ASTParser('cpp') +code = Path('tests/assets/gencpp_samples/ast.hpp').read_text() +tree = parser.get_cached_tree(None, code) +code_bytes = code.encode('utf8') +def find_node(node): + start = node.start_point.row + 1 + if start == 296: + print(f'LINE 296 NODE: {node.type}, NAME: {parser._get_name(node, code_bytes)}, TEXT: {code_bytes[node.start_byte:node.end_byte].decode("utf8")[:50].strip()}...') + for child in node.children: + find_node(child) +find_node(tree.root_node) diff --git a/gencpp_manual_slop_template.toml b/gencpp_manual_slop_template.toml new file mode 100644 index 0000000..bae088a --- /dev/null +++ b/gencpp_manual_slop_template.toml @@ -0,0 +1,44 @@ +# manual_slop.toml - GenCpp Project Configuration + +[project] +name = "gencpp" +git_dir = ".git" +main_context = "GenCpp: A C++ library for agenic code generation and analysis." +word_wrap = true + +[paths] +logs_dir = ".manual_slop/logs" +scripts_dir = ".manual_slop/scripts" + +[files] +base_dir = "." +paths = [] + +[screenshots] +base_dir = ".manual_slop/screenshots" +paths = [] + +[output] +output_dir = ".manual_slop/md_gen" + +[conductor] +dir = ".manual_slop/conductor" + +[agent] +roles = ["User", "AI", "Vendor API", "System", "Reasoning", "Context"] + +[agent.tools] +read_file = true +list_directory = true +search_files = true +grep_search = true +run_shell_command = true +# ... other tools ... + +[discussion] +active = "main" +auto_add = true + +[discussion.discussions.main] +history = [] +context_snapshot = [] diff --git a/mock_debug_prompt.txt b/mock_debug_prompt.txt index c225260..edf7f01 100644 --- a/mock_debug_prompt.txt +++ b/mock_debug_prompt.txt @@ -120,3 +120,26 @@ ERROR: file not found: C:\Users\Ed\AppData\Local\Temp\tmpbtllj21_\final_test_2.p What makes RAG great? ------------------ +--- MOCK INVOKED --- +ARGS: ['tests/mock_gemini_cli.py'] +PROMPT: +PATH: Epic Initialization — please produce tracks +------------------ +--- MOCK INVOKED --- +ARGS: ['tests/mock_gemini_cli.py'] +PROMPT: +Please generate the implementation tickets for this track. +------------------ +--- MOCK INVOKED --- +ARGS: ['tests/mock_gemini_cli.py'] +PROMPT: +Please read test.txt +You are assigned to Ticket T1. +Task Description: do something +------------------ +--- MOCK INVOKED --- +ARGS: ['tests/mock_gemini_cli.py'] +PROMPT: +role: tool +Here are the results: {"content": "done"} +------------------ diff --git a/project_history.toml b/project_history.toml index 980697f..c77c4b8 100644 --- a/project_history.toml +++ b/project_history.toml @@ -9,5 +9,5 @@ active = "main" [discussions.main] git_commit = "" -last_updated = "2026-05-10T12:15:18" +last_updated = "2026-05-10T14:38:49" history = [] diff --git a/src/file_cache.py b/src/file_cache.py index c4822e1..5b5918a 100644 --- a/src/file_cache.py +++ b/src/file_cache.py @@ -106,18 +106,29 @@ class ASTParser: if name_node: return code_bytes[name_node.start_byte:name_node.end_byte].decode("utf8", errors="replace") - if node.type in ("function_definition", "field_declaration"): + if node.type in ("function_definition", "field_declaration", "declaration"): def find_id(n: tree_sitter.Node) -> str: - if n.type in ("identifier", "field_identifier", "qualified_identifier", "destructor_name"): + # In C++, prefer function_declarator or operator_name + if self.language_name in ("cpp", "c"): + # Look for declarator field or child + decl = n.child_by_field_name("declarator") + if decl: + # If it's a function_declarator, the name is inside its 'declarator' or 'name' + if decl.type == "function_declarator": + nested_decl = decl.child_by_field_name("declarator") + if nested_decl: + return find_id(nested_decl) + elif decl.type == "pointer_declarator": + return find_id(decl) + elif decl.type in ("identifier", "field_identifier", "qualified_identifier", "operator_name", "destructor_name"): + return code_bytes[decl.start_byte:decl.end_byte].decode("utf8", errors="replace") + + if n.type in ("identifier", "field_identifier", "qualified_identifier", "operator_name", "destructor_name"): return code_bytes[n.start_byte:n.end_byte].decode("utf8", errors="replace") - # Try field name 'declarator' first - d = n.child_by_field_name("declarator") - if d: - res = find_id(d) - if res: return res - # Fallback to all children + + # Fallback to children, but avoid bodies and types for child in n.children: - if child.type == "compound_statement": continue # Don't look in body + if child.type in ("compound_statement", "field_declaration_list", "class_body", "declaration_list", "enum_body", "type_identifier", "primitive_type"): continue res = find_id(child) if res: return res return "" @@ -125,12 +136,12 @@ class ASTParser: if node.type == "template_declaration": for child in node.children: - if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "field_declaration"): + if child.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "field_declaration", "declaration"): return self._get_name(child, code_bytes) if node.type in ("struct_specifier", "class_specifier", "class_definition", "enum_specifier", "enum_definition", "namespace_definition"): for child in node.children: - if child.type in ("type_identifier", "identifier", "namespace_identifier"): + if child.type in ("type_identifier", "identifier", "namespace_identifier", "qualified_identifier"): return code_bytes[child.start_byte:child.end_byte].decode("utf8", errors="replace") return "" @@ -442,12 +453,11 @@ class ASTParser: parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: - """ - [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] - """ if not target_parts: return None target = target_parts[0] + best_match = None + for child in node.children: # If it's a field_declaration, it might wrap a class/struct/enum definition check_node = child @@ -457,12 +467,16 @@ class ASTParser: check_node = sub break - is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration") + is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration", "declaration") if is_interesting: node_name = self._get_name(check_node, code_bytes) if node_name == target: if len(target_parts) == 1: - return check_node if child.type != "field_declaration" else child + match = check_node if child.type != "field_declaration" else child + if match.child_by_field_name("body"): + return match + if not best_match: + best_match = match next_parts = target_parts[1:] else: next_parts = target_parts @@ -475,28 +489,44 @@ class ASTParser: break if body: found = walk(body, next_parts) - if found: return found + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found for sub in check_node.children: if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"): found = walk(sub, next_parts) - if found: return found - elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found + elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body", "preproc_if", "preproc_ifdef", "preproc_else", "preproc_elif"): found = walk(child, target_parts) - if found: return found - return None + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found + return best_match def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: - if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration"): + best = None + if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "declaration"): if self._get_name(node, code_bytes) == target: - return node + if node.child_by_field_name("body"): + return node + best = node for child in node.children: res = deep_search(child, target) - if res: return res - return None + if res: + if res.child_by_field_name("body"): + return res + if not best: + best = res + return best found_node = walk(tree.root_node, parts) - if not found_node: - found_node = deep_search(tree.root_node, name) + if not found_node or not found_node.child_by_field_name("body"): + alt = deep_search(tree.root_node, name) + if alt: + if not found_node or alt.child_by_field_name("body"): + found_node = alt if found_node: return code_bytes[found_node.start_byte:found_node.end_byte].decode("utf8", errors="replace") @@ -515,12 +545,11 @@ class ASTParser: parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: - """ - [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] - """ if not target_parts: return None target = target_parts[0] + best_match = None + for child in node.children: # If it's a field_declaration, it might wrap a class/struct/enum definition check_node = child @@ -530,12 +559,16 @@ class ASTParser: check_node = sub break - is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration") + is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration", "declaration") if is_interesting: node_name = self._get_name(check_node, code_bytes) if node_name == target: if len(target_parts) == 1: - return check_node if child.type != "field_declaration" else child + match = check_node if child.type != "field_declaration" else child + if match.child_by_field_name("body"): + return match + if not best_match: + best_match = match next_parts = target_parts[1:] else: next_parts = target_parts @@ -548,28 +581,44 @@ class ASTParser: break if body: found = walk(body, next_parts) - if found: return found + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found for sub in check_node.children: if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"): found = walk(sub, next_parts) - if found: return found - elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found + elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body", "preproc_if", "preproc_ifdef", "preproc_else", "preproc_elif"): found = walk(child, target_parts) - if found: return found - return None + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found + return best_match def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: - if node.type in ("function_definition", "template_declaration"): + best = None + if node.type in ("function_definition", "template_declaration", "declaration"): if self._get_name(node, code_bytes) == target: - return node + if node.child_by_field_name("body"): + return node + best = node for child in node.children: res = deep_search(child, target) - if res: return res - return None + if res: + if res.child_by_field_name("body"): + return res + if not best: + best = res + return best found_node = walk(tree.root_node, parts) - if not found_node: - found_node = deep_search(tree.root_node, name) + if not found_node or not found_node.child_by_field_name("body"): + alt = deep_search(tree.root_node, name) + if alt: + if not found_node or alt.child_by_field_name("body"): + found_node = alt if found_node: target_node = found_node @@ -639,12 +688,11 @@ class ASTParser: parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: - """ - [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] - """ if not target_parts: return None target = target_parts[0] + best_match = None + for child in node.children: # If it's a field_declaration, it might wrap a class/struct/enum definition check_node = child @@ -654,12 +702,16 @@ class ASTParser: check_node = sub break - is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration") + is_interesting = check_node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "field_declaration", "declaration") if is_interesting: node_name = self._get_name(check_node, code_bytes) if node_name == target: if len(target_parts) == 1: - return check_node if child.type != "field_declaration" else child + match = check_node if child.type != "field_declaration" else child + if match.child_by_field_name("body"): + return match + if not best_match: + best_match = match next_parts = target_parts[1:] else: next_parts = target_parts @@ -672,28 +724,44 @@ class ASTParser: break if body: found = walk(body, next_parts) - if found: return found + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found for sub in check_node.children: if sub.type in ("field_declaration_list", "class_body", "declaration_list", "enum_body"): found = walk(sub, next_parts) - if found: return found - elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body"): + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found + elif child.type in ("module", "translation_unit", "namespace_definition", "declaration_list", "field_declaration_list", "class_body", "preproc_if", "preproc_ifdef", "preproc_else", "preproc_elif"): found = walk(child, target_parts) - if found: return found - return None + if found: + if found.child_by_field_name("body"): return found + if not best_match: best_match = found + return best_match def deep_search(node: tree_sitter.Node, target: str) -> Optional[tree_sitter.Node]: - if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration"): + best = None + if node.type in ("function_definition", "class_definition", "class_specifier", "struct_specifier", "enum_specifier", "enum_definition", "namespace_definition", "template_declaration", "declaration"): if self._get_name(node, code_bytes) == target: - return node + if node.child_by_field_name("body"): + return node + best = node for child in node.children: res = deep_search(child, target) - if res: return res - return None + if res: + if res.child_by_field_name("body"): + return res + if not best: + best = res + return best found_node = walk(tree.root_node, parts) - if not found_node: - found_node = deep_search(tree.root_node, name) + if not found_node or not found_node.child_by_field_name("body"): + alt = deep_search(tree.root_node, name) + if alt: + if not found_node or alt.child_by_field_name("body"): + found_node = alt if found_node: code_bytearray = bytearray(code_bytes) diff --git a/test_output.txt b/test_output.txt new file mode 100644 index 0000000..51834ae Binary files /dev/null and b/test_output.txt differ diff --git a/tests/test_ast_masking_core.py b/tests/test_ast_masking_core.py new file mode 100644 index 0000000..e07db5a --- /dev/null +++ b/tests/test_ast_masking_core.py @@ -0,0 +1,44 @@ +import sys +import os +from pathlib import Path +sys.path.append(str(Path(__file__).parent.parent)) + +from src.mcp_client import ts_cpp_get_definition, ts_cpp_get_signature, ts_cpp_get_code_outline +from src.aggregate import build_tier3_context + +def test_ast_masking_gencpp_samples(): + ast_hpp = "tests/assets/gencpp_samples/ast.hpp" + + # 1. Verify we can get a complex definition despite forward declarations + # Currently it might return the forward declaration, let's check + defn = ts_cpp_get_definition(ast_hpp, "AST") + print(f"--- AST Definition (len {len(defn)}) ---") + print(defn[:100] + "...") + + # 2. Test Masking logic in aggregation + item = { + "path": Path(ast_hpp), + "entry": ast_hpp, + "content": Path(ast_hpp).read_text(encoding="utf-8"), + "ast_mask": {"AST": "def", "Code::append": "sig"}, + "auto_aggregate": True, + "force_full": False, + "tier": None + } + + res = build_tier3_context([item], Path("."), [], [], []) + print("--- MASKED AGGREGATION RESULT ---") + # Search for Code::append signature in result + if "forceinline void append(Code other)" in res: + print("SUCCESS: Code::append signature found!") + else: + print("FAILURE: Code::append signature NOT found!") + # Print a bit of what IS there + print(res[2800:3500]) + + assert "(Masked)" in res + assert "struct AST" in res + assert "forceinline void append(Code other)" in res + +if __name__ == "__main__": + test_ast_masking_gencpp_samples()