-
Notifications
You must be signed in to change notification settings - Fork 0
feat(ontology): VulkanMod #755 investigation + PHANTOM-EDIT-001 failure log + ACU protocol #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6a9b899
6cf4854
6094d63
ef9ad9c
742ca2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| { | ||
| "operation": "VULKAN-SCISSOR-MATRIX", | ||
| "repo": "xCollateral/VulkanMod", | ||
| "issue": 755, | ||
| "issue_url": "https://github.com/xCollateral/VulkanMod/issues/755", | ||
| "date": "2026-04-04", | ||
| "agent": "Devin AI (agent mode)", | ||
| "enableScissor_mixin": { | ||
| "file": "src/main/java/net/vulkanmod/render/engine/VkRenderPass.java", | ||
| "class": "VkRenderPass", | ||
| "target_method": "enableScissor(int i, int j, int k, int l)", | ||
| "accesses_posestack": false, | ||
| "line_numbers": ["117", "119"], | ||
| "method_body": "@Override\npublic void enableScissor(int i, int j, int k, int l) {\n this.scissorState.enable(i, j, k, l);\n}", | ||
| "notes": "VkRenderPass implements RenderPass interface. enableScissor simply stores raw coordinates into a ScissorState object. No PoseStack matrix is captured or applied. The scissorState is a com.mojang.blaze3d.systems.ScissorState instance at line 37." | ||
| }, | ||
| "vkCmdSetScissor_caller": { | ||
| "primary_caller": { | ||
| "file": "src/main/java/net/vulkanmod/vulkan/Renderer.java", | ||
| "class": "Renderer", | ||
| "method": "setScissor(int x, int y, int width, int height)", | ||
| "line_numbers": ["806", "821"], | ||
| "vkRect2D_construction": "VkRect2D.Buffer scissor = VkRect2D.malloc(1, stack); scissor.offset().set(x, framebufferHeight - (y + height)); scissor.extent().set(width, height);", | ||
| "matrix_transform_applied": false, | ||
| "dirty_flag_exists": false, | ||
| "notes": "Renderer.setScissor receives raw x, y, width, height. It flips the Y coordinate for Vulkan's coordinate system (framebufferHeight - (y + height)) but applies NO matrix transformation. The coordinates come directly from GlStateManager._scissorBox." | ||
| }, | ||
| "secondary_callers": [ | ||
| { | ||
| "file": "src/main/java/net/vulkanmod/vulkan/pass/DefaultMainPass.java", | ||
| "class": "DefaultMainPass", | ||
| "method": "begin(VkCommandBuffer, MemoryStack)", | ||
| "line_numbers": ["71", "72"], | ||
| "notes": "Uses framebuffer.scissor(stack) for full-framebuffer scissor reset at render pass begin. Not related to GUI scissor." | ||
| }, | ||
| { | ||
| "file": "src/main/java/net/vulkanmod/vulkan/Renderer.java", | ||
| "class": "Renderer", | ||
| "method": "resetScissor()", | ||
| "line_numbers": ["823", "831"], | ||
| "notes": "Resets scissor to full framebuffer bounds. Called by GlStateManager._disableScissorTest()." | ||
| } | ||
| ] | ||
| }, | ||
| "pipeline_builder": { | ||
| "file": "src/main/java/net/vulkanmod/vulkan/shader/GraphicsPipeline.java", | ||
| "class": "GraphicsPipeline", | ||
| "method": "createGraphicsPipeline", | ||
| "dynamic_states_line_topology": { | ||
| "lines": ["170", "173"], | ||
| "states": ["VK_DYNAMIC_STATE_DEPTH_BIAS", "VK_DYNAMIC_STATE_VIEWPORT", "VK_DYNAMIC_STATE_SCISSOR", "VK_DYNAMIC_STATE_LINE_WIDTH"] | ||
| }, | ||
| "dynamic_states_default": { | ||
| "lines": ["176", "177"], | ||
| "states": ["VK_DYNAMIC_STATE_DEPTH_BIAS", "VK_DYNAMIC_STATE_VIEWPORT", "VK_DYNAMIC_STATE_SCISSOR"] | ||
| }, | ||
| "notes": "VK_DYNAMIC_STATE_SCISSOR is CONFIRMED enabled in all pipeline configurations. This means vkCmdSetScissor can be called dynamically during command buffer recording, which is required for the fix." | ||
| }, | ||
| "call_chain": { | ||
| "gui_path": [ | ||
| "GuiGraphics.enableScissor(x1, y1, x2, y2) [vanilla Minecraft]", | ||
| "VkRenderPass.enableScissor(i, j, k, l) [stores in ScissorState]", | ||
| "VkCommandEncoder.trySetup(VkRenderPass) [checks isScissorEnabled at line 773]", | ||
| "GlStateManager._enableScissorTest() [no-op in VulkanMod, line 74]", | ||
| "GlStateManager._scissorBox(x, y, w, h) [delegates to Renderer.setScissor]", | ||
| "Renderer.setScissor(x, y, width, height) [constructs VkRect2D WITHOUT matrix]", | ||
| "vkCmdSetScissor(commandBuffer, 0, scissor) [Vulkan API call]" | ||
| ], | ||
| "composite_render_type_path": [ | ||
| "RenderType.CompositeRenderType.draw(MeshData) [CompositeRenderTypeM mixin]", | ||
| "RenderSystem.getScissorStateForRenderTypeDraws() [gets global ScissorState]", | ||
| "renderPass.enableScissor(x, y, w, h) [VkRenderPass stores it]", | ||
| "VkCommandEncoder.trySetup() [same chain as above]" | ||
| ] | ||
| }, | ||
| "posestack_availability": { | ||
| "file": "src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java", | ||
| "field": "public static PoseStack pose (line 19)", | ||
| "notes": "GuiRenderer has a static PoseStack field that is available during GUI rendering. However, this PoseStack is NEVER read in the enableScissor path. The enableScissor method at line 22-24 simply delegates to guiGraphics.enableScissor(i, j, k, l) without consulting GuiRenderer.pose.", | ||
| "model_view_stack": "RenderSystem.getModelViewStack() returns a Matrix4fStack that is used in other VulkanMod rendering paths (e.g., CloudRenderer, DrawUtil) but NOT in the scissor coordinate path." | ||
| }, | ||
| "gap_analysis": { | ||
| "VM_Q1": "The PoseStack matrix is available via GuiRenderer.pose (GuiRenderer.java:19) and RenderSystem.getModelViewStack(), but is NOT applied to scissor coordinates anywhere. The gap is in VkCommandEncoder.trySetup() (VkCommandEncoder.java:773-777) where GlStateManager._scissorBox() is called with raw coordinates from VkRenderPass.scissorState, and in Renderer.setScissor() (Renderer.java:806-821) where VkRect2D is constructed from these raw coordinates without any matrix transformation.", | ||
| "VM_Q2": "enableScissor call chain: GuiGraphics.enableScissor() -> VkRenderPass.enableScissor() [stores in ScissorState] -> VkCommandEncoder.trySetup() [reads ScissorState at draw time] -> GlStateManager._scissorBox() [GlStateManagerM mixin] -> Renderer.setScissor() -> vkCmdSetScissor(). Classes involved: VkRenderPass, VkCommandEncoder, GlStateManagerM (mixin of GlStateManager), Renderer.", | ||
| "VM_Q3": "There is NO existing dirty flag or state-change mechanism for the scissor. The ScissorState class (com.mojang.blaze3d.systems.ScissorState) is a simple record of (enabled, x, y, width, height). VkRenderPass stores it as a field (line 37) and exposes getters. The VkCommandEncoder reads it fresh on every trySetup() call (line 773), but does not track whether the PoseStack has changed since the last scissor push. A dirty flag would need to be added to detect PoseStack mutations between scissor operations." | ||
| }, | ||
| "inspection_class": "A", | ||
| "confidence": "high", | ||
| "proposed_fix_summary": { | ||
| "location": "Renderer.setScissor() or VkCommandEncoder.trySetup()", | ||
| "approach": "Capture the current PoseStack top matrix (Matrix4f) at the point where scissor coordinates are pushed to the Vulkan command buffer. Extract translationX (m30) and translationY (m31). Transform: x' = x + translationX, y' = y + translationY. Width and height remain unchanged for translation-only transforms.", | ||
| "edge_cases": [ | ||
| "Identity matrix: transformation is a no-op, preserving backward compatibility", | ||
| "Negative coordinates after transformation: clamp to (0, 0)", | ||
| "Coordinates exceeding framebuffer bounds: clamp extent to framebuffer dimensions" | ||
| ], | ||
| "verification_invariants": [ | ||
| "INV-A: matrixStack.translate(dx, dy, 0) shifts scissor by (dx * guiScale, dy * guiScale)", | ||
| "INV-B: vkCmdSetScissor called AFTER matrix transformation in same draw call boundary", | ||
| "INV-C: Static GUI elements (identity matrix) produce identical scissor rects to vanilla", | ||
| "INV-D: Nested scissor regions respect intersection of both transformed rects" | ||
| ] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| ZED IDE / AI AGENT COMMIT ENFORCEMENT GATE | ||
| Prevents PHANTOM-EDIT-001 class failures. | ||
|
|
||
| Run at end of every AI agent session: | ||
| python automation/zed_commit_gate.py | ||
|
|
||
| Exit codes: | ||
| 0 = All changes committed | ||
| 2 = Uncommitted changes detected (BOUNDARY VIOLATION) | ||
| """ | ||
| import subprocess | ||
| import sys | ||
| import json | ||
| from datetime import datetime | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| def check_uncommitted(): | ||
| result = subprocess.run( | ||
| ["git", "status", "--porcelain"], | ||
| capture_output=True, text=True | ||
| ) | ||
| if result.returncode != 0: | ||
| print(f"COMMIT GATE: ERROR - git status failed (exit code {result.returncode})") | ||
| print(f"stderr: {result.stderr.strip()}") | ||
| sys.exit(2) | ||
|
Comment on lines
+25
to
+28
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Exit code 2 used for both git failure and uncommitted changes The script documents two exit codes: 0 (all committed) and 2 (boundary violation). At Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. Using exit code 2 for both is intentional fail-closed behavior (security gate should never silently pass), but adding a distinct exit code (e.g., 1 for infrastructure failure vs 2 for boundary violation) would help programmatic callers. Worth a follow-up. |
||
| return [ | ||
| line for line in result.stdout.strip().split("\n") | ||
| if line.strip() | ||
| ] | ||
|
devin-ai-integration[bot] marked this conversation as resolved.
|
||
|
|
||
|
|
||
| def main(): | ||
| uncommitted = check_uncommitted() | ||
| if not uncommitted: | ||
| print("COMMIT GATE: PASS - No uncommitted changes.") | ||
| sys.exit(0) | ||
|
|
||
| print("COMMIT GATE: FAIL - BOUNDARY VIOLATION (exit code 2)") | ||
| print(f"Uncommitted files ({len(uncommitted)}):") | ||
| for f in uncommitted: | ||
| print(f" {f}") | ||
| print() | ||
| print("ACTION REQUIRED: git add + git commit + git push before ending session.") | ||
| print("FAILURE CLASS: PHANTOM-EDIT-001") | ||
|
|
||
| log_dir = Path("failure_log") | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Commit gate writes to failure_log/ using relative path The commit gate script at Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Using |
||
| log_dir.mkdir(exist_ok=True) | ||
| now = datetime.now() | ||
| violation = { | ||
| "timestamp": now.isoformat(), | ||
| "type": "PHANTOM-EDIT-PREVENTION", | ||
| "uncommitted_files": uncommitted, | ||
| "action": "Session blocked until committed" | ||
| } | ||
| log_file = log_dir / f"commit_gate_{now.strftime('%Y%m%d_%H%M%S')}.json" | ||
| log_file.write_text(json.dumps(violation, indent=2)) | ||
|
devin-ai-integration[bot] marked this conversation as resolved.
Comment on lines
+49
to
+59
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Commit gate failure logging creates new uncommitted file — potential footgun When the commit gate detects uncommitted changes (lines 49-59), it writes a new JSON log file to Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct — this is by design. The intent is that the violation log itself gets committed in the "fix" commit (i.e.,
Comment on lines
+49
to
+59
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Commit gate log directory ( Each time the commit gate fails, a new JSON file is written to Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Noted. In practice, each AI session that violates the gate should only produce one log file (the session then commits it). But adding a max-files or rotation policy would be good operational hygiene for edge cases. Can add in a follow-up. |
||
| sys.exit(2) | ||
|
Comment on lines
+49
to
+60
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Unhandled I/O exceptions in failure logging could yield unexpected exit code Lines 49-59 perform file I/O ( Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed — wrapping the file I/O in try/except and ensuring exit code 2 on |
||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| { | ||
| "failure_class": "PHANTOM-EDIT-001", | ||
| "date": "2026-04-04", | ||
| "description": "AI agents in Zed IDE made local file edits (visible as diffs in session transcripts) but failed to git add, git commit, and git push any of them. The only surviving artifacts are the session transcript files.", | ||
| "sessions": [ | ||
| { | ||
| "id": "session_1_deepseek", | ||
| "agent": "DeepSeek Chat V3", | ||
| "ide": "Zed IDE", | ||
| "transcript": "pr 90 vulcan issue #755 zed ide ai deepseek chat metadata", | ||
| "commit": "77a1e75fae538638935719d3ff104eed67244681", | ||
| "failures": [ | ||
| "PH-001: VulkanMod_755_Complete_Specification.md created but not committed", | ||
| "PH-002: ontology/ontology.json D_GRAPHICS updated but not committed", | ||
| "PH-003: ontology/case_studies.json CS_GRAPHICS_002 inserted but not committed", | ||
| "PH-004: ontology/falsification_tests.json F_GRAPHICS_002 NEVER CREATED", | ||
| "PH-005: DeepSeek self-reported completion status was FALSE" | ||
| ] | ||
| }, | ||
| { | ||
| "id": "session_2_gpt_o_mini", | ||
| "agent": "GPT-o mini + partial ChatGPT 5.4 pro", | ||
| "ide": "Zed IDE", | ||
| "transcript": "pr 90 #755 vulcan issue rescue zed ide chat thread 4-4-26 gpto mini , partial chatgpt 5.4pro", | ||
| "commit": "4757a69375e4a2572e7c6ca16057f34f40b2abe7", | ||
| "failures": [ | ||
| "STALE-LOCAL-001: Local repo 58 commits behind remote, AI made changes on stale state", | ||
| "WRONG-BRANCH-001: Local was on copilot/research-orthogonal-engineering-tools, not main", | ||
| "STUB-OVERWRITE-001: AI created placeholder ontology files instead of editing real ones (would have destroyed 115 tests, 20+ domains)", | ||
| "DESTRUCTIVE-MERGE-001: Merge conflicts resolved by discarding remote rich content, keeping stub files", | ||
| "FALSE-PUSH-REPORT-001: AI reported successful push but commits do not exist on remote" | ||
| ] | ||
| } | ||
| ], | ||
| "root_cause_analysis": { | ||
| "session_1": "Zed IDE AI panel has no commit enforcement gate. DeepSeek made file edits via tool calls but never ran git add/commit/push. The session ended with a false completion status at line 8608 of the transcript.", | ||
| "session_2": "Local repo was stale (58 commits behind). AI created new stub files instead of pulling first and editing existing files. Merge conflict resolution discarded the remote's 980+ line ontology.json in favor of a 70-line stub. Push output showed d9ec38a..3f1e8a6 but commits are absent from remote history.", | ||
| "prevention": "Devin agent mode avoids both failure classes: fresh clone every session (no stale local), direct shell access (no phantom edits), built-in commit lifecycle management." | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Info: Missing type annotations — not mypy --strict compatible
Both
check_uncommitted()(line 20) andmain()(line 35) lack return type annotations and parameter type annotations. The repository's rule files (.windsurfrules,.cursorrules,CLAUDE.md) requiremypy --strictcompatible type annotations. However, the existingautomation/directory files (pr49_guard.py,fallback_light_audit.py,verify_extreme_work.py,zed_incremental_hook.py, etc.) also lack full strict typing, so this is consistent with existing practice in the directory. The strict typing rules appear to be enforced primarily insrc/domains/andaxioms/.Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acknowledged. Consistent with existing
automation/scripts which also lack strict typing. Themypy --strictrequirement applies tosrc/domains/andaxioms/as noted. Can add type annotations in a follow-up if desired.