fix: Replace Codeberg dependency with local Forgejo instance (#611)

- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
  creates admin + bot users (dev-bot, review-bot), generates API
  tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
  FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
  FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
  FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
  →forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
  FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-23 16:57:12 +00:00
parent 39d30faf45
commit a66bd91721
58 changed files with 863 additions and 628 deletions

View file

@ -24,12 +24,12 @@ a `mode` field. Two modes are supported:
In this mode, skip the normal tech-debt grooming pipeline. Instead:
a. Fetch the target issue:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<target_issue>"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<target_issue>"
b. Fetch ALL comments on the target issue to understand scope and
prior bounce reasons:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<target_issue>/comments?limit=50"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<target_issue>/comments?limit=50"
c. Read the affected files listed in the issue body to understand
the actual code scope.
d. Break the issue into 2-5 sub-issues, each sized for a single
@ -63,8 +63,8 @@ description = """
This step only runs in grooming mode. Skip if in breakdown mode.
Fetch all open tech-debt issues:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?type=issues&state=open&limit=50" | \
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?type=issues&state=open&limit=50" | \
jq '[.[] | select(.labels | map(.name) | any(. == "tech-debt"))]'
For each issue compute a triage score:
@ -94,14 +94,14 @@ These are issues that block backlog items but are not themselves labeled backlog
The dev-agent is completely starved until they are promoted or resolved.
For each tier-0 issue:
- Read the full body: curl -sf -H "Authorization: token $CODEBERG_TOKEN" "$CODEBERG_API/issues/{number}"
- Read the full body: curl -sf -H "Authorization: token $FORGE_TOKEN" "$FORGE_API/issues/{number}"
- If resolvable: promote to backlog add acceptance criteria, affected files, relabel
- If needs human decision: add to ESCALATE block
- If invalid / wontfix: close with explanation comment
After completing all tier-0, re-fetch to check for new blockers:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?type=issues&state=open&limit=50" | \
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?type=issues&state=open&limit=50" | \
jq '[.[] | select(.labels | map(.name) | any(. == "tech-debt"))]'
If new tier-0 blockers appeared, process those too.
@ -172,8 +172,8 @@ id = "verify"
title = "Verify completion and loop until zero tech-debt"
description = """
Re-fetch ALL open tech-debt issues and count them:
REMAINING=$(curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?type=issues&state=open&limit=50" | \
REMAINING=$(curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?type=issues&state=open&limit=50" | \
jq '[.[] | select(.labels | map(.name) | any(. == "tech-debt"))] | length')
echo "Remaining tech-debt: $REMAINING"

View file

@ -78,25 +78,25 @@ If you discover pre-existing issues (NOT introduced by this PR), create
tech-debt issues via API so they are tracked separately:
# Look up tech-debt label ID (create if missing):
TECH_DEBT_ID=$(curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/labels" | jq -r '.[] | select(.name=="tech-debt") | .id')
TECH_DEBT_ID=$(curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/labels" | jq -r '.[] | select(.name=="tech-debt") | .id')
if [ -z "$TECH_DEBT_ID" ]; then
TECH_DEBT_ID=$(curl -sf -X POST \
-H "Authorization: token $CODEBERG_TOKEN" \
-H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/labels" \
"$FORGE_API/labels" \
-d '{"name":"tech-debt","color":"#6B7280","description":"Pre-existing tech debt flagged by AI review"}' | jq -r '.id')
fi
# Check for duplicate before creating:
EXISTING=$(curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=open&labels=tech-debt&limit=50" | \
EXISTING=$(curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=open&labels=tech-debt&limit=50" | \
jq --arg t "TITLE" '[.[] | select(.title == $t)] | length')
# Create only if no duplicate:
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
-H "Content-Type: application/json" "$CODEBERG_API/issues" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" "$FORGE_API/issues" \
-d '{"title":"...","body":"Flagged by AI reviewer in PR #NNN.\n\n## Problem\n...\n\n---\n*Auto-created from AI review*","labels":[TECH_DEBT_ID]}'
Only create follow-ups for clear, actionable tech debt. Do not create
@ -138,5 +138,5 @@ For a re-review, structure the markdown as:
After writing the JSON file, signal completion:
echo "PHASE:done" > "$PHASE_FILE"
Then STOP and wait. The orchestrator will post your review to Codeberg.
Then STOP and wait. The orchestrator will post your review to the forge.
"""

View file

@ -56,8 +56,8 @@ Groom the open issue backlog. This step is the core Claude-driven analysis
Pre-checks (bash, zero tokens detect problems before invoking Claude):
1. Fetch all open issues:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=open&type=issues&limit=50&sort=updated&direction=desc"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=open&type=issues&limit=50&sort=updated&direction=desc"
2. Duplicate detection: compare issue titles pairwise. Normalize
(lowercase, strip prefixes like feat:/fix:/refactor:, collapse whitespace)
@ -162,7 +162,7 @@ Sibling dependency rule (CRITICAL):
If either section is missing:
a. Write a comment action to the manifest:
echo '{"action":"comment","issue":NNN,"body":"This issue is missing required sections. Please use the issue templates at `.codeberg/ISSUE_TEMPLATE/` — needs: <missing items>."}' >> "$PROJECT_REPO_ROOT/gardener/pending-actions.jsonl"
echo '{"action":"comment","issue":NNN,"body":"This issue is missing required sections. Please use the issue templates at `.forgejo/ISSUE_TEMPLATE/` — needs: <missing items>."}' >> "$PROJECT_REPO_ROOT/gardener/pending-actions.jsonl"
Where <missing items> is a comma-separated list of what's absent
(e.g. "acceptance criteria, affected files" or just "affected files").
b. Write a remove_label action to the manifest:
@ -249,19 +249,19 @@ Review all issues labeled 'blocked' and decide their fate.
(See issue #352 for the blocked label convention.)
1. Fetch all blocked issues:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=open&type=issues&labels=blocked&limit=50"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=open&type=issues&labels=blocked&limit=50"
2. For each blocked issue, read the full body and comments:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<number>"
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<number>/comments"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<number>"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<number>/comments"
3. Check dependencies extract issue numbers from ## Dependencies /
## Depends on / ## Blocked by sections. For each dependency:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<dep_number>"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<dep_number>"
Check if the dependency is now closed.
4. For each blocked issue, choose ONE action:
@ -459,9 +459,9 @@ executes them after the PR merges.
git push -u origin "$BRANCH"
g. Create a PR:
PR_RESPONSE=$(curl -sf -X POST \
-H "Authorization: token $CODEBERG_TOKEN" \
-H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/pulls" \
"$FORGE_API/pulls" \
-d '{"title":"chore: gardener housekeeping",
"head":"'"$BRANCH"'","base":"'"$PRIMARY_BRANCH"'",
"body":"Automated gardener housekeeping — AGENTS.md updates + pending actions manifest.\\n\\nReview `gardener/pending-actions.json` for proposed grooming actions (label changes, closures, comments). These execute after merge."}')

View file

@ -60,8 +60,8 @@ Evidence from the preflight step informs whether each prediction is valid
(e.g. "red-team stale since March 12" is confirmed by evidence/ timestamps).
1. Fetch unreviewed predictions:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=open&type=issues&labels=prediction%2Funreviewed&limit=50"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=open&type=issues&labels=prediction%2Funreviewed&limit=50"
If there are none, note that and skip to step 3b (label resolution
is still required the file-at-constraints step needs label IDs).
@ -72,10 +72,10 @@ Evidence from the preflight step informs whether each prediction is valid
Project formulas are dispatched via action issues on the project repo.
3. Fetch all open issues to check for overlap:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=open&type=issues&limit=50"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=open&type=issues&limit=50"
3b. Resolve label IDs needed for triage AND filing (fetch via $CODEBERG_API/labels).
3b. Resolve label IDs needed for triage AND filing (fetch via $FORGE_API/labels).
ALWAYS execute this step, even if there are no predictions to triage
the file-at-constraints step depends on these IDs:
- <unreviewed_label_id> prediction/unreviewed
@ -120,65 +120,65 @@ Evidence from the preflight step informs whether each prediction is valid
Example body structure:
## Problem\n<what the prediction identified>\n\n## Proposed solution\n<approach>\n\n## Affected files\n- <file1>\n- <file2>\n\n## Acceptance criteria\n- [ ] <criterion 1>\n- [ ] CI green
Create the issue:
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
-H "Content-Type: application/json" "$CODEBERG_API/issues" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" "$FORGE_API/issues" \
-d '{"title":"...","body":"...","labels":[<label_id>]}'
Extract the issue number from the response (jq -r '.number').
a2. Verify the label was applied (Codeberg may silently drop labels
a2. Verify the label was applied (the forge may silently drop labels
on creation). Re-apply via a separate POST if missing:
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<new_issue_num>/labels" \
"$FORGE_API/issues/<new_issue_num>/labels" \
-d '{"labels":[<label_id>]}'
b. Comment on the prediction with "Actioned as #NNN — <reasoning>":
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<pred_num>/comments" \
"$FORGE_API/issues/<pred_num>/comments" \
-d '{"body":"Actioned as #NNN — <reasoning>"}'
c. Relabel: remove prediction/unreviewed, add prediction/actioned:
curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<pred_num>/labels/<unreviewed_label_id>"
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X DELETE -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<pred_num>/labels/<unreviewed_label_id>"
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<pred_num>/labels" \
"$FORGE_API/issues/<pred_num>/labels" \
-d '{"labels":[<actioned_label_id>]}'
d. Close the prediction:
curl -sf -X PATCH -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X PATCH -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<pred_num>" \
"$FORGE_API/issues/<pred_num>" \
-d '{"state":"closed"}'
For WATCH:
a. Comment with reasoning why not urgent:
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<pred_num>/comments" \
"$FORGE_API/issues/<pred_num>/comments" \
-d '{"body":"Watching — <reasoning>"}'
b. Replace prediction/unreviewed label with prediction/backlog:
curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<pred_num>/labels/<unreviewed_label_id>"
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X DELETE -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<pred_num>/labels/<unreviewed_label_id>"
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<pred_num>/labels" \
"$FORGE_API/issues/<pred_num>/labels" \
-d '{"labels":[<prediction_backlog_label_id>]}'
For DISMISS:
a. Comment with explicit reasoning:
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<pred_num>/comments" \
"$FORGE_API/issues/<pred_num>/comments" \
-d '{"body":"Dismissed — <reasoning>"}'
b. Relabel: remove prediction/unreviewed, add prediction/actioned:
curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<pred_num>/labels/<unreviewed_label_id>"
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X DELETE -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<pred_num>/labels/<unreviewed_label_id>"
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<pred_num>/labels" \
"$FORGE_API/issues/<pred_num>/labels" \
-d '{"labels":[<actioned_label_id>]}'
c. Close the prediction:
curl -sf -X PATCH -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X PATCH -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<pred_num>" \
"$FORGE_API/issues/<pred_num>" \
-d '{"state":"closed"}'
6. Track promoted predictions they are added to the prerequisite tree
@ -208,8 +208,8 @@ Read these inputs:
- $PROJECT_REPO_ROOT/formulas/*.toml project-specific formulas
- Open issues (fetched via API, or reuse from prediction-triage)
- Closed issues (fetch recently closed to detect resolved prerequisites):
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=closed&type=issues&limit=50&sort=updated&direction=desc"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=closed&type=issues&limit=50&sort=updated&direction=desc"
- Planner memory (loaded in preflight)
- Promoted predictions from prediction-triage (add as prerequisites if relevant)
@ -218,8 +218,8 @@ Read these inputs:
For each issue referenced in the prerequisite tree (by #number), fetch its
recent comments to detect signals that the issue is stuck or bouncing:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<number>/comments?limit=10"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<number>/comments?limit=10"
Scan each comment body for these signals:
@ -394,17 +394,17 @@ Filing gate — for each constraint (that is NOT stuck):
## Problem\n<what this prerequisite is and which objectives it blocks>\n\n## Proposed solution\n<rough approach>\n\n## Affected files\n- <file1>\n- <file2>\n\n## Acceptance criteria\n- [ ] <criterion derived from the constraint>\n- [ ] CI green\n\n## Dependencies\n- #NNN (if depends on other open issues)
Create the issue:
curl -sf -X POST \
-H "Authorization: token $CODEBERG_TOKEN" \
-H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues" \
"$FORGE_API/issues" \
-d '{"title":"...","body":"...","labels":[<backlog_label_id>]}'
Extract the issue number from the response (jq -r '.number').
2b. Verify the label was applied (Codeberg may silently drop labels
2b. Verify the label was applied (the forge may silently drop labels
on creation). Always re-apply via a separate POST to be safe:
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<new_issue_num>/labels" \
"$FORGE_API/issues/<new_issue_num>/labels" \
-d '{"labels":[<backlog_label_id>]}'
3. If an issue already exists and is open, skip it no duplicate filing.
@ -422,20 +422,20 @@ is purely additive.
5. **Add `priority` to top-5 constraint issues:**
For each of the top 5 constraint issues (whether just filed or already
existing), check if it already has the `priority` label. If not, add it:
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues/<issue_number>/labels" \
"$FORGE_API/issues/<issue_number>/labels" \
-d '{"labels":[<priority_label_id>]}'
6. **Remove `priority` from issues no longer in top 5:**
Fetch all open issues that currently have the `priority` label:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=open&labels=priority&type=issues&limit=50"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=open&labels=priority&type=issues&limit=50"
For each issue in this list that is NOT one of the current top 5
constraint issues, remove the `priority` label (demote back to plain
`backlog`):
curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues/<issue_number>/labels/<priority_label_id>"
curl -sf -X DELETE -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues/<issue_number>/labels/<priority_label_id>"
This keeps the priority set current only the active bottleneck issues
get priority, not stale constraints from previous runs.
@ -634,9 +634,9 @@ run — only file changes (tree, journal, MEMORY.md) need the PR.
git push -u origin "$BRANCH"
g. Create a PR:
curl -sf -X POST \
-H "Authorization: token $CODEBERG_TOKEN" \
-H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/pulls" \
"$FORGE_API/pulls" \
-d '{"title":"chore: planner run — prerequisite tree update",
"head":"<branch>","base":"<primary-branch>",
"body":"Automated planner run — prerequisite tree update and journal entry."}'

View file

@ -3,7 +3,7 @@
# Goal: find the project's biggest weakness. Explore when uncertain,
# exploit when confident (dispatch a formula to prove the theory).
#
# Memory: previous predictions on Codeberg ARE the memory.
# Memory: previous predictions on the forge ARE the memory.
# No separate memory file — the issue tracker is the source of truth.
#
# Executed by predictor/predictor-run.sh via cron — no action issues.
@ -33,12 +33,12 @@ Set up the working environment and load your prediction history.
git pull --ff-only origin "$PRIMARY_BRANCH" --quiet
2. Fetch ALL your previous predictions (open + recently closed):
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=open&type=issues&labels=prediction%2Funreviewed&limit=50"
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=open&type=issues&labels=prediction%2Fbacklog&limit=50"
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/issues?state=closed&type=issues&labels=prediction%2Factioned&limit=50&sort=updated&direction=desc"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=open&type=issues&labels=prediction%2Funreviewed&limit=50"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=open&type=issues&labels=prediction%2Fbacklog&limit=50"
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/issues?state=closed&type=issues&labels=prediction%2Factioned&limit=50&sort=updated&direction=desc"
For each prediction, note:
- What you predicted (title + body)
@ -150,21 +150,21 @@ For each weakness you identify, choose one:
## Filing
1. Look up label IDs:
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/labels" | jq '[.[] | select(.name | startswith("prediction")) | {name, id}]'
curl -sf -H "Authorization: token $CODEBERG_TOKEN" \
"$CODEBERG_API/labels" | jq '.[] | select(.name == "action") | .id'
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/labels" | jq '[.[] | select(.name | startswith("prediction")) | {name, id}]'
curl -sf -H "Authorization: token $FORGE_TOKEN" \
"$FORGE_API/labels" | jq '.[] | select(.name == "action") | .id'
2. File predictions:
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues" \
"$FORGE_API/issues" \
-d '{"title":"<title>","body":"<body>","labels":[<prediction_unreviewed_id>]}'
3. File action dispatches (if exploiting):
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
curl -sf -X POST -H "Authorization: token $FORGE_TOKEN" \
-H "Content-Type: application/json" \
"$CODEBERG_API/issues" \
"$FORGE_API/issues" \
-d '{"title":"action: test prediction #NNN — <formula> <focus>","body":"<body>","labels":[<action_label_id>]}'
4. Do NOT duplicate existing open predictions. If your theory matches