diff --git a/.github/workflows/autoloop.lock.yml b/.github/workflows/autoloop.lock.yml
index f8a6564..3cd4ee4 100644
--- a/.github/workflows/autoloop.lock.yml
+++ b/.github/workflows/autoloop.lock.yml
@@ -23,7 +23,7 @@
# An iterative optimization loop inspired by Karpathy's Autoresearch and Claude Code's /loop.
# Runs on a configurable schedule to autonomously improve a target artifact toward a measurable goal.
# Each iteration: reads the program definition, proposes a change, evaluates against a metric,
-# and accepts or rejects the change. Tracks all iterations in a rolling GitHub issue.
+# and accepts or rejects the change.
# - User defines the optimization goal and evaluation criteria in a program.md file
# - Accepts changes only when they improve the metric (ratchet pattern)
# - Persists all state via repo-memory (human-readable, human-editable)
@@ -36,7 +36,7 @@
# Imports:
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"c63aa1e1ccbd4cd47d9d18200ed0141d013ea22a3ae98d3efae2563cfda919c4","compiler_version":"v0.65.6","strict":true,"agent_id":"copilot"}
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"261bf9da368e23a161bfa806b9ff11c0b6e8777ac493b6e8ac0a7586b6a046b5","compiler_version":"v0.65.6","strict":true,"agent_id":"copilot"}
name: "Autoloop"
"on":
@@ -227,21 +227,21 @@ jobs:
run: |
bash ${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh
{
- cat << 'GH_AW_PROMPT_25eda8a50024125e_EOF'
+ cat << 'GH_AW_PROMPT_0171e714d32c535f_EOF'
- GH_AW_PROMPT_25eda8a50024125e_EOF
+ GH_AW_PROMPT_0171e714d32c535f_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/repo_memory_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_25eda8a50024125e_EOF'
+ cat << 'GH_AW_PROMPT_0171e714d32c535f_EOF'
- Tools: add_comment(max:7), create_issue(max:1), update_issue(max:3), create_pull_request(max:2), add_labels(max:2), remove_labels(max:2), push_to_pull_request_branch(max:2), missing_tool, missing_data, noop
- GH_AW_PROMPT_25eda8a50024125e_EOF
+ Tools: add_comment(max:7), create_issue, update_issue(max:3), create_pull_request(max:2), add_labels(max:2), remove_labels(max:2), push_to_pull_request_branch(max:2), missing_tool, missing_data, noop
+ GH_AW_PROMPT_0171e714d32c535f_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md"
- cat << 'GH_AW_PROMPT_25eda8a50024125e_EOF'
+ cat << 'GH_AW_PROMPT_0171e714d32c535f_EOF'
The following GitHub context information is available for this workflow:
@@ -274,7 +274,7 @@ jobs:
- **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches).
- GH_AW_PROMPT_25eda8a50024125e_EOF
+ GH_AW_PROMPT_0171e714d32c535f_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md"
@@ -282,11 +282,11 @@ jobs:
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_push_to_pr_branch_guidance.md"
fi
- cat << 'GH_AW_PROMPT_25eda8a50024125e_EOF'
+ cat << 'GH_AW_PROMPT_0171e714d32c535f_EOF'
{{#runtime-import .github/workflows/shared/reporting.md}}
{{#runtime-import .github/workflows/autoloop.md}}
- GH_AW_PROMPT_25eda8a50024125e_EOF
+ GH_AW_PROMPT_0171e714d32c535f_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -418,6 +418,10 @@ jobs:
run: |
header=$(printf "x-access-token:%s" "${GH_AW_FETCH_TOKEN}" | base64 -w 0)
git -c "http.extraheader=Authorization: Basic ${header}" fetch origin '+refs/heads/*:refs/remotes/origin/*'
+ - name: Setup Python
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ with:
+ python-version: '3.12'
- name: Create gh-aw temp directory
run: bash ${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh
- name: Configure gh CLI for GitHub Enterprise
@@ -429,8 +433,7 @@ jobs:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_TOKEN: ${{ github.token }}
name: Check which programs are due
- run: |
- python3 .github/workflows/scripts/autoloop_scheduler.py
+ run: python3 .github/workflows/scripts/autoloop_scheduler.py
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
@@ -489,12 +492,12 @@ jobs:
mkdir -p ${RUNNER_TEMP}/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_4cb88c61a21f151b_EOF'
+ cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_78a2a2ed2efbd98a_EOF'
{"add_comment":{"hide_older_comments":false,"max":7,"target":"*"},"add_labels":{"max":2,"target":"*"},"create_issue":{"labels":["automation","autoloop"],"max":1,"title_prefix":"[Autoloop] "},"create_pull_request":{"draft":true,"labels":["automation","autoloop"],"max":2,"max_patch_size":1024,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS"],"protected_files_policy":"fallback-to-issue","protected_path_prefixes":[".github/",".agents/"],"title_prefix":"[Autoloop] "},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]},"push_to_pull_request_branch":{"if_no_changes":"warn","max":2,"max_patch_size":1024,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS"],"protected_path_prefixes":[".github/",".agents/"],"target":"*","title_prefix":"[Autoloop] "},"remove_labels":{"max":2,"target":"*"},"update_issue":{"allow_body":true,"max":3,"target":"*","title_prefix":"[Autoloop] "}}
- GH_AW_SAFE_OUTPUTS_CONFIG_4cb88c61a21f151b_EOF
+ GH_AW_SAFE_OUTPUTS_CONFIG_78a2a2ed2efbd98a_EOF
- name: Write Safe Outputs Tools
run: |
- cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_e890c86d61e62c6e_EOF'
+ cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_26e30063dd6b7824_EOF'
{
"description_suffixes": {
"add_comment": " CONSTRAINTS: Maximum 7 comment(s) can be added. Target: *.",
@@ -508,8 +511,8 @@ jobs:
"repo_params": {},
"dynamic_tools": []
}
- GH_AW_SAFE_OUTPUTS_TOOLS_META_e890c86d61e62c6e_EOF
- cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_713ceb1e276ac547_EOF'
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_26e30063dd6b7824_EOF
+ cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_53d20ba1aa3186df_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -768,7 +771,7 @@ jobs:
"customValidation": "requiresOneOf:status,title,body"
}
}
- GH_AW_SAFE_OUTPUTS_VALIDATION_713ceb1e276ac547_EOF
+ GH_AW_SAFE_OUTPUTS_VALIDATION_53d20ba1aa3186df_EOF
node ${RUNNER_TEMP}/gh-aw/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
@@ -838,7 +841,7 @@ jobs:
export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.11'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_d7bba620cd55956d_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_e86cff10886b39f3_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -879,7 +882,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_d7bba620cd55956d_EOF
+ GH_AW_MCP_CONFIG_e86cff10886b39f3_EOF
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
@@ -1544,7 +1547,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.gradle-enterprise.cloud,*.jsr.io,*.pythonhosted.org,*.vsblob.vsassets.io,adoptium.net,anaconda.org,api.adoptium.net,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.foojay.io,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.nuget.org,api.snapcraft.io,archive.apache.org,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,bun.sh,cdn.azul.com,cdn.jsdelivr.net,central.sonatype.com,ci.dot.net,conda.anaconda.org,conda.binstar.org,crates.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,deb.nodesource.com,deno.land,develocity.apache.org,dist.nuget.org,dl.google.com,dlcdn.apache.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.java.net,download.oracle.com,downloads.gradle-dn.com,esm.sh,files.pythonhosted.org,ge.spockframework.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,gradle.org,host.docker.internal,index.crates.io,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,maven-central.storage-download.googleapis.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkgs.dev.azure.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.gradle.org,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo.yarnpkg.com,repo1.maven.org,repository.apache.org,s.symcb.com,s.symcd.com,scans-in.gradle.com,security.ubuntu.com,services.gradle.org,sh.rustup.rs,skimdb.npmjs.com,static.crates.io,static.rust-lang.org,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.java.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":false,\"max\":7,\"target\":\"*\"},\"add_labels\":{\"max\":2,\"target\":\"*\"},\"create_issue\":{\"labels\":[\"automation\",\"autoloop\"],\"max\":2,\"title_prefix\":\"[Autoloop] \"},\"create_pull_request\":{\"draft\":true,\"labels\":[\"automation\",\"autoloop\"],\"max\":2,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_files_policy\":\"fallback-to-issue\",\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[Autoloop] \"},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":2,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"target\":\"*\",\"title_prefix\":\"[Autoloop] \"},\"remove_labels\":{\"max\":2,\"target\":\"*\"},\"update_issue\":{\"allow_body\":true,\"max\":3,\"target\":\"*\",\"title_prefix\":\"[Autoloop] \"}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":false,\"max\":7,\"target\":\"*\"},\"add_labels\":{\"max\":2,\"target\":\"*\"},\"create_issue\":{\"labels\":[\"automation\",\"autoloop\"],\"max\":1,\"title_prefix\":\"[Autoloop] \"},\"create_pull_request\":{\"draft\":true,\"labels\":[\"automation\",\"autoloop\"],\"max\":2,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_files_policy\":\"fallback-to-issue\",\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[Autoloop] \"},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":2,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"target\":\"*\",\"title_prefix\":\"[Autoloop] \"},\"remove_labels\":{\"max\":2,\"target\":\"*\"},\"update_issue\":{\"allow_body\":true,\"max\":3,\"target\":\"*\",\"title_prefix\":\"[Autoloop] \"}}"
GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/scripts/autoloop_scheduler.py b/.github/workflows/scripts/autoloop_scheduler.py
index d29509b..827e3f7 100644
--- a/.github/workflows/scripts/autoloop_scheduler.py
+++ b/.github/workflows/scripts/autoloop_scheduler.py
@@ -57,6 +57,13 @@
OUTPUT_DIR = "/tmp/gh-aw"
OUTPUT_FILE = os.path.join(OUTPUT_DIR, "autoloop.json")
+# Default repo-memory ``max-file-size`` for state files. Mirrors the value
+# configured under ``tools.repo-memory.max-file-size`` in
+# ``workflows/autoloop.md``. Surfaced in the scheduler output so the agent
+# prompt can reason about the rolling-compaction budget without re-parsing
+# workflow frontmatter.
+STATE_FILE_MAX_BYTES = 30720
+
# ---------------------------------------------------------------------------
# Pure helpers (unit-tested directly)
@@ -214,6 +221,22 @@ def read_program_state(program_name, repo_memory_dir=REPO_MEMORY_DIR):
return parse_machine_state(content)
+def get_state_file_size(program_name, repo_memory_dir=REPO_MEMORY_DIR):
+ """Return the size of the program's state file in bytes (0 if missing).
+
+ Surfaced in ``autoloop.json`` as ``state_file_size_bytes`` so the agent
+ can decide whether to compact the state file aggressively this iteration
+ (see the rolling-compaction rule in ``workflows/autoloop.md``'s
+ "Update Rules" section).
+ """
+ state_file = os.path.join(repo_memory_dir, "{}.md".format(program_name))
+ try:
+ st = os.stat(state_file)
+ except OSError:
+ return 0
+ return st.st_size
+
+
def _bootstrap_template_if_missing():
"""Create ``.autoloop/programs/example.md`` if the directory is missing."""
if os.path.isdir(PROGRAMS_DIR):
@@ -622,6 +645,8 @@ def main():
"selected_file": selected_file,
"selected_issue": selected_issue,
"selected_target_metric": selected_target_metric,
+ "state_file_size_bytes": get_state_file_size(selected) if selected else 0,
+ "state_file_max_bytes": STATE_FILE_MAX_BYTES,
"issue_programs": {
name: info["issue_number"] for name, info in issue_programs.items()
},
diff --git a/workflows/autoloop.md b/workflows/autoloop.md
index fe5b570..5f14579 100644
--- a/workflows/autoloop.md
+++ b/workflows/autoloop.md
@@ -77,6 +77,11 @@ tools:
repo-memory:
branch-name: memory/autoloop
file-glob: ["*.md"]
+ # 30 KB per state file -- enough for the structured sections plus ~10 most-recent
+ # iteration entries plus ~5 compressed-range summaries. The rolling-compaction
+ # rule in "Update Rules" below keeps files under this budget. Tune up for
+ # short-cadence programs (e.g. `every 5m`); tune down for daily-cadence ones.
+ max-file-size: 30720
imports:
- shared/reporting.md
@@ -189,6 +194,8 @@ The pre-step has already determined which program to run. Read `/tmp/gh-aw/autol
- **`selected_file`**: The full path to the program's markdown file (either `.autoloop/programs//program.md`, `.autoloop/programs/.md`, or `/tmp/gh-aw/issue-programs/.md` for issue-based programs).
- **`selected_issue`**: The GitHub issue number if the selected program came from an issue, or `null` if it came from a file.
- **`selected_target_metric`**: The `target-metric` value from the program's frontmatter (a number), or `null` if the program is open-ended. Used to check the [halting condition](#halting-condition) after each accepted iteration.
+- **`state_file_size_bytes`**: Current size of the selected program's state file in bytes (0 if it does not exist yet). Use this together with `state_file_max_bytes` to decide whether to compact aggressively this iteration (see [Update Rules](#update-rules) — when size exceeds 80% of the max, collapse older iteration entries).
+- **`state_file_max_bytes`**: The configured `max-file-size` for repo-memory state files (default `30720`, i.e. 30 KB). Files larger than this are rejected by repo-memory, breaking scheduling.
- **`issue_programs`**: A mapping of program name → issue number for all discovered issue-based programs.
- **`deferred`**: Other programs that were due but will be handled in future runs.
- **`unconfigured`**: Programs that still have the sentinel or placeholder content.
@@ -804,6 +811,13 @@ After each iteration, prepend an entry to the **📊 Iteration History** section
- **Add to Foreclosed Avenues** only when an approach is conclusively ruled out (not just rejected once).
- **Respect Current Priorities** — if a maintainer has written priorities, follow them in your next proposal.
- **Write the state file** to the repo-memory folder. Changes are automatically committed and pushed to the `memory/autoloop` branch after the workflow completes.
+- **Keep the state file compact.** The state file must stay under the configured `max-file-size` (default 30 KB — see `state_file_max_bytes` in `/tmp/gh-aw/autoloop.json`). When prepending a new iteration entry, collapse older iteration entries (beyond the most recent 10) into compressed summary lines. Example format for collapsed entries:
+
+ ```markdown
+ ### Iters 50–100 — ✅ (metrics 20→55): brief summary of what worked across this range
+ ```
+
+ Also prune **📚 Lessons Learned** to the most recent and most relevant entries, and consolidate similar entries in **🚧 Foreclosed Avenues** if it grows beyond a page. If `state_file_size_bytes` from `/tmp/gh-aw/autoloop.json` is already greater than 80% of `state_file_max_bytes`, **compact aggressively** this iteration: collapse to the most recent 5 detailed entries and merge older compressed ranges into broader bands. Repo-memory rejects files larger than `max-file-size`, which breaks scheduling — so keeping the file under budget is mandatory, not optional.
## Guidelines
diff --git a/workflows/scripts/autoloop_scheduler.py b/workflows/scripts/autoloop_scheduler.py
index d29509b..827e3f7 100644
--- a/workflows/scripts/autoloop_scheduler.py
+++ b/workflows/scripts/autoloop_scheduler.py
@@ -57,6 +57,13 @@
OUTPUT_DIR = "/tmp/gh-aw"
OUTPUT_FILE = os.path.join(OUTPUT_DIR, "autoloop.json")
+# Default repo-memory ``max-file-size`` for state files. Mirrors the value
+# configured under ``tools.repo-memory.max-file-size`` in
+# ``workflows/autoloop.md``. Surfaced in the scheduler output so the agent
+# prompt can reason about the rolling-compaction budget without re-parsing
+# workflow frontmatter.
+STATE_FILE_MAX_BYTES = 30720
+
# ---------------------------------------------------------------------------
# Pure helpers (unit-tested directly)
@@ -214,6 +221,22 @@ def read_program_state(program_name, repo_memory_dir=REPO_MEMORY_DIR):
return parse_machine_state(content)
+def get_state_file_size(program_name, repo_memory_dir=REPO_MEMORY_DIR):
+ """Return the size of the program's state file in bytes (0 if missing).
+
+ Surfaced in ``autoloop.json`` as ``state_file_size_bytes`` so the agent
+ can decide whether to compact the state file aggressively this iteration
+ (see the rolling-compaction rule in ``workflows/autoloop.md``'s
+ "Update Rules" section).
+ """
+ state_file = os.path.join(repo_memory_dir, "{}.md".format(program_name))
+ try:
+ st = os.stat(state_file)
+ except OSError:
+ return 0
+ return st.st_size
+
+
def _bootstrap_template_if_missing():
"""Create ``.autoloop/programs/example.md`` if the directory is missing."""
if os.path.isdir(PROGRAMS_DIR):
@@ -622,6 +645,8 @@ def main():
"selected_file": selected_file,
"selected_issue": selected_issue,
"selected_target_metric": selected_target_metric,
+ "state_file_size_bytes": get_state_file_size(selected) if selected else 0,
+ "state_file_max_bytes": STATE_FILE_MAX_BYTES,
"issue_programs": {
name: info["issue_number"] for name, info in issue_programs.items()
},