Skip to content

feat: add agentic GitHub Actions workflows (real-LLM, FSM, living-code) - #13

Open
spiralgang wants to merge 1 commit into
mainfrom
feat/agentic-gha-workflows
Open

feat: add agentic GitHub Actions workflows (real-LLM, FSM, living-code)#13
spiralgang wants to merge 1 commit into
mainfrom
feat/agentic-gha-workflows

Conversation

@spiralgang

Copy link
Copy Markdown
Owner

Agentic GitHub Actions workflows derived from your ~/ markdown layouts.

  • 17 workflows: 6-stage self-driving SDLC loop (examine→plan→build→review→fix→approve), FSM self-compile, living-code matrix, GLM companion, ARIA autorepair, edge codespace, omniscient IDE safety gate, Zhipu orchestrator, termux-ai skills, device-privacy build, main CI, watchdog.
  • Shared real-LLM composite action (.github/actions/llm/action.yml): no mock — fails loudly without LLM_API_KEY / ZHIPU_API_KEY.
  • Seccomp sandbox reusable workflow (json-py.md).
  • Every workflow calls a REAL model and adapts to the repo (stages that need local files skip gracefully).

Required secrets (add in repo Settings → Secrets): LLM_API_KEY, LLM_BASE_URL (optional), LLM_MODEL (optional), ZHIPU_API_KEY.

Triggers:
gh workflow run sdlc-examine.yml -f cycle=0
gh workflow run livingcode-matrix.yml -f children=4 -f cycles=2

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a real LLM call composite action, a reusable seccomp sandbox runner workflow, and a consolidated 'living-code' agentic engine in Python. The review feedback highlights several critical issues, including an invalid GitHub Actions context variable, a potential command injection vulnerability in the composite action, a malformed JSON string in the seccomp profile, and several robustness and logic bugs in the Python engine (such as unhandled exceptions in the thread pool, missing namespace exposure for the json module, redundant LLM calls, and potential FileNotFoundError on empty directory paths).

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +81 to +82
PROMPT_FILE: ${{ env.RUNNER_TEMP }}/llm/prompt.txt
SYSTEM_FILE: ${{ env.RUNNER_TEMP }}/llm/system.txt

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The context ${{ env.RUNNER_TEMP }} is invalid because default environment variables like RUNNER_TEMP are not populated in the GitHub Actions env context. This will evaluate to an empty string, causing the action to attempt to read/write to /llm/prompt.txt and fail due to permission errors. Use ${{ runner.temp }} instead.

        PROMPT_FILE: ${{ runner.temp }}/llm/prompt.txt
        SYSTEM_FILE: ${{ runner.temp }}/llm/system.txt

Comment on lines +58 to +70
- name: "Write prompt + system to files"
id: write
shell: bash
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/llm"
cat > "$RUNNER_TEMP/llm/prompt.txt" <<'PROMPT_EOF'
${{ inputs.prompt }}
PROMPT_EOF
cat > "$RUNNER_TEMP/llm/system.txt" <<'SYSTEM_EOF'
${{ inputs.system }}
SYSTEM_EOF
echo "wrote prompt + system"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Using inline expression interpolation (${{ inputs.prompt }}) inside a shell script heredoc can lead to syntax errors or command injection vulnerabilities if the prompt contains special characters or the heredoc delimiter (PROMPT_EOF). It is highly recommended to pass these inputs via environment variables and write them to files using printf.

    - name: "Write prompt + system to files"
      id: write
      shell: bash
      env:
        PROMPT_CONTENT: ${{ inputs.prompt }}
        SYSTEM_CONTENT: ${{ inputs.system }}
      run: |
        set -euo pipefail
        mkdir -p "$RUNNER_TEMP/llm"
        printf "%s" "$PROMPT_CONTENT" > "$RUNNER_TEMP/llm/prompt.txt"
        printf "%s" "$SYSTEM_CONTENT" > "$RUNNER_TEMP/llm/system.txt"
        echo "wrote prompt + system"

Comment on lines +57 to +58
{"names": ["read","write","open","openat","close","stat","fstat","lstat","poll","select","
epoll_wait","fork","vfork","clone","execve","exit","exit_group","wait4","nanosleep","getpid",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The JSON string literal contains a newline between "select"," and epoll_wait",". In JSON, literal newlines inside string values are invalid and will cause parsing to fail. Ensure each string is fully enclosed on its own line, with the comma outside the quotes.

              {"names": ["read","write","open","openat","close","stat","fstat","lstat","poll","select",
          "epoll_wait","fork","vfork","clone","execve","exit","exit_group","wait4","nanosleep","getpid",

self.model = model or os.getenv("LLM_MODEL", "glm-4-flash")
self.base_url = (base_url or os.getenv("LLM_BASE_URL",
"https://open.bigmodel.cn/api/paas/v4")).rstrip("/")
self.api_key = api_key if api_key is not None else os.getenv("LLM_API_KEY", "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The PR description mentions ZHIPU_API_KEY as a required secret, and the default base URL points to Zhipu AI's endpoint. However, LLMClient only checks LLM_API_KEY. Adding a fallback to ZHIPU_API_KEY will prevent unexpected authentication failures for users who configured that secret.

Suggested change
self.api_key = api_key if api_key is not None else os.getenv("LLM_API_KEY", "")
self.api_key = api_key if api_key is not None else (os.getenv("LLM_API_KEY") or os.getenv("ZHIPU_API_KEY") or "")

Comment on lines +122 to +123
# expose the real LLM + json to the child namespace (it calls self.llm.chat)
mod.__dict__["llm"] = llm

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment states that json is exposed to the child namespace, but only llm is actually added to mod.__dict__. Since import statements are banned by the AST audit, child agents have no way to use the json module unless it is explicitly exposed here.

Suggested change
# expose the real LLM + json to the child namespace (it calls self.llm.chat)
mod.__dict__["llm"] = llm
# expose the real LLM + json to the child namespace (it calls self.llm.chat)
mod.__dict__["llm"] = llm
mod.__dict__["json"] = json

Comment on lines +170 to +172
def _run(src):
inst = compile_child(src, llm)
return inst.execute(task)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since child agents are generated dynamically by an LLM, they are highly prone to syntax errors, AST audit failures, or runtime exceptions. If any child execution fails, the unhandled exception will crash the entire deploy_children thread pool execution. Wrapping the execution in a try-except block will make the manager run much more robust.

Suggested change
def _run(src):
inst = compile_child(src, llm)
return inst.execute(task)
def _run(src):
try:
inst = compile_child(src, llm)
return inst.execute(task)
except Exception as e:
LOG.warning("Child execution failed: %s", e)
return f"Error: {e}"

Comment on lines +182 to +201
spec = llm.chat([
{"role": "system", "content": "You emit Python child agents. "
"When asked, output a fenced ```python block defining "
"class GeneratedAgent with __init__(self, role, goal, llm) and "
"execute(self, task: dict) -> str that calls self.llm.chat(...)."},
{"role": "user", "content": f"WRITE A PYTHON CHILD AGENT for: {base_task}"},
], max_tokens=800)
m = re.search(r"```python\n(.*?)```", spec, re.DOTALL)
if not m:
LOG.warning("manager did not emit a child agent this cycle"); continue
src = m.group(1)
# optional mutation: ask to improve the previous child
if history:
imp = llm.chat([
{"role": "user", "content": f"Improve this child agent:\n{history[-1]}\n"
"Return ONLY a new ```python ... ``` block."}], max_tokens=800)
mm = re.search(r"```python\n(.*?)```", imp, re.DOTALL)
if mm:
src = mm.group(1)
history.append(src)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In living_code_cycle, a new child agent spec is generated via llm.chat at the start of every cycle. However, if history is not empty, spec is completely overwritten by the mutated/improved version of the previous agent (imp). This wastes LLM API calls and tokens. Consider only generating the initial agent in the first cycle, and mutating the existing history in subsequent cycles.

STAGE: {state}
{STAGE_PROMPTS[state]}
"""
os.makedirs(os.path.dirname(path), exist_ok=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If path does not contain a directory component (e.g., if it is just a filename like superlab-generated.yml), os.path.dirname(path) will return an empty string "". Calling os.makedirs("", exist_ok=True) will raise a FileNotFoundError. Check if the directory name is non-empty before calling os.makedirs.

Suggested change
os.makedirs(os.path.dirname(path), exist_ok=True)
dirname = os.path.dirname(path)
if dirname:
os.makedirs(dirname, exist_ok=True)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant