Agent self-bootstrap: auto-open a scaffold PR instead of dumping onboarding on the human #304

Closed
opened 2026-06-30 21:32:57 +00:00 by igor · 2 comments
Collaborator

Goal

When a repo fails validation on agent-authorable scaffolding, open a scaffold PR instead of (only) dumping an onboarding ticket on the human. Turns "repo not ready, you set it up" into "merge this one PR." The worked example, done by hand, is scenekids.com #12 — that PR is the literal target output.

Why

validate_repo_via_api (lib/repo-checks.sh) checks: CLAUDE.md, README, test signal, lint config, CI workflow, labels. Of these, CLAUDE.md + test script + lint config are agent-authorable — none is operator-only. But handle_onboarding_failure assumes "scaffolding only the operator can add" (its own comment) and files a human ticket. That assumption is wrong, and it's why ~8 repos sit with "repo not ready" tickets on the human's plate (scenekids was 5 tickets that collapsed to 1 PR once scaffolded).

Design

In/around handle_onboarding_failure (lib/repo-checks.sh:233), after parsing the gap report:

  1. Detect the agent-doable gaps from the - [ ] ... report lines: missing CLAUDE.md, test signal, lint config.
  2. Detect the stack (start with the portfolio: Node + Eleventy/11ty static sites — package.json present, eleventy in deps/scripts). Bail to ticket-only on anything unrecognized.
  3. Generate the scaffold for the gaps:
    • CLAUDE.md — conventions from package.json (name/description/scripts) + README + root listing. (v1 can template this deterministically; a model-written version is a nice v2.)
    • package.json testeleventy --dryrun for 11ty (a clean build is the gate); generalize per build tool.
    • .markdownlint.json — lenient lint config.
  4. Open a scaffold PR (branch feat/agent-scaffold, marker-titled), assigned to FORGEJO_REVIEWER. Idempotent: skip if that branch/PR already exists (don't re-open every tick).
  5. Narrow the onboarding ticket: note "agent scaffolded CLAUDE.md/test/lint in PR #N; only the CI workflow is left for you" — because .forgejo/workflows/ is off-limits to the autonomous agent, so the scaffold PR can't include CI. (Repos that already have a CI workflow — like scenekids' deploy.yml — validate fully on the scaffold PR alone; repos with none keep a one-line "add CI" residual.)

Acceptance

  • A new unvalidated 11ty repo gets an auto-opened scaffold PR matching scenekids #12 (minus the CI file).
  • Merging it makes the repo validate (if it had a CI workflow already) or leaves only "add CI" for the human.
  • The scaffold PR is idempotent (one per repo, not re-opened each tick) and goes to the human's merge gate — never auto-lands.
  • Unit test: gap-report parsing + stack detection + the idempotency skip.

Notes

  • His merge gates it twice over (the feature PR, and every scaffold PR it emits), so it's safe to land behind review.
  • Pairs with the CEO-as-triage-gate idea (separate ticket) — together they're what gets every repo to "only the human's plate is left."
## Goal When a repo fails validation on **agent-authorable** scaffolding, open a scaffold PR instead of (only) dumping an onboarding ticket on the human. Turns "repo not ready, you set it up" into "merge this one PR." The worked example, done by hand, is **scenekids.com #12** — that PR is the literal target output. ## Why `validate_repo_via_api` (`lib/repo-checks.sh`) checks: CLAUDE.md, README, test signal, lint config, CI workflow, labels. Of these, **CLAUDE.md + test script + lint config are agent-authorable** — none is operator-only. But `handle_onboarding_failure` assumes "scaffolding only the operator can add" (its own comment) and files a human ticket. That assumption is wrong, and it's why ~8 repos sit with "repo not ready" tickets on the human's plate (scenekids was 5 tickets that collapsed to 1 PR once scaffolded). ## Design In/around `handle_onboarding_failure` (`lib/repo-checks.sh:233`), after parsing the gap report: 1. **Detect the agent-doable gaps** from the `- [ ] ...` report lines: missing CLAUDE.md, test signal, lint config. 2. **Detect the stack** (start with the portfolio: Node + Eleventy/11ty static sites — `package.json` present, eleventy in deps/scripts). Bail to ticket-only on anything unrecognized. 3. **Generate the scaffold** for the gaps: - `CLAUDE.md` — conventions from `package.json` (name/description/scripts) + README + root listing. (v1 can template this deterministically; a model-written version is a nice v2.) - `package.json` `test` — `eleventy --dryrun` for 11ty (a clean build is the gate); generalize per build tool. - `.markdownlint.json` — lenient lint config. 4. **Open a scaffold PR** (branch `feat/agent-scaffold`, marker-titled), assigned to `FORGEJO_REVIEWER`. Idempotent: skip if that branch/PR already exists (don't re-open every tick). 5. **Narrow the onboarding ticket**: note "agent scaffolded CLAUDE.md/test/lint in PR #N; only the CI workflow is left for you" — because **`.forgejo/workflows/` is off-limits to the autonomous agent**, so the scaffold PR can't include CI. (Repos that already have a CI workflow — like scenekids' `deploy.yml` — validate fully on the scaffold PR alone; repos with none keep a one-line "add CI" residual.) ## Acceptance - A new unvalidated 11ty repo gets an auto-opened scaffold PR matching scenekids #12 (minus the CI file). - Merging it makes the repo validate (if it had a CI workflow already) or leaves only "add CI" for the human. - The scaffold PR is idempotent (one per repo, not re-opened each tick) and goes to the human's merge gate — never auto-lands. - Unit test: gap-report parsing + stack detection + the idempotency skip. ## Notes - His merge gates it twice over (the feature PR, and every scaffold PR it emits), so it's safe to land behind review. - Pairs with the **CEO-as-triage-gate** idea (separate ticket) — together they're what gets every repo to "only the human's plate is left."
Author
Collaborator

Implementation plan (locked after reading lib/repo-checks.sh)

Giving this its own focused pass — it edits the live onboarding path (handle_onboarding_failure, repo-checks.sh:233) which self-deploys in ~1 min, so it gets the same careful/tested treatment the P0 (#307) got, not a wedge between other work.

Shape: a new lib/scaffold.sh (pure + impure split for testability), called from handle_onboarding_failure before the ticket step. Additive and fully guarded: any failure or unrecognized stack falls through to today's ticket-filing unchanged (blast radius = worst case, behaves exactly as now).

Pure functions (unit-tested in bin/test-scaffold.sh):

  • scaffold_parse_gaps <report> — from the - [ ] <name> -- <hint> lines, return which agent-authorable gaps are open: CLAUDE.md, test signal, lint config. (README/labels/CI are NOT auto-scaffolded — CI is off-limits .forgejo/workflows/.)
  • scaffold_detect_stack — Node + Eleventy (package.json present + eleventy in deps/scripts). Bail (empty) on anything else → ticket-only.
  • scaffold_gen_claude_md / scaffold_gen_markdownlint / scaffold_add_test_script — deterministic content from package.json (name/scripts) + README. Test signal for 11ty = a \"test\": \"eleventy --dryrun\"-style script (clean build is the gate).

Impure (guarded, integration):

  • Idempotency: skip if feat/agent-scaffold branch or its PR already exists (don't re-open every tick).
  • Create the files on feat/agent-scaffold via the contents API (no clone), one PUT each; package.json is a read-jq-write update with its blob sha.
  • Open the scaffold PR assigned to FORGEJO_REVIEWER; narrow the onboarding ticket to "agent scaffolded CLAUDE.md/test/lint in PR #N — only the CI workflow is left for you."

Acceptance: a new unvalidated 11ty repo gets an auto scaffold PR matching scenekids #12 (minus CI); merging it validates the repo (if CI already exists) or leaves only "add CI"; idempotent; never auto-lands (human merge gate). Main live beneficiary: parsley (certifiedtradejobs is a slow-burn, not urgent).

PR next.

## Implementation plan (locked after reading `lib/repo-checks.sh`) Giving this its own focused pass — it edits the live onboarding path (`handle_onboarding_failure`, repo-checks.sh:233) which self-deploys in ~1 min, so it gets the same careful/tested treatment the P0 (#307) got, not a wedge between other work. **Shape:** a new `lib/scaffold.sh` (pure + impure split for testability), called from `handle_onboarding_failure` **before** the ticket step. Additive and fully guarded: any failure or unrecognized stack falls through to today's ticket-filing unchanged (blast radius = worst case, behaves exactly as now). **Pure functions (unit-tested in `bin/test-scaffold.sh`):** - `scaffold_parse_gaps <report>` — from the `- [ ] <name> -- <hint>` lines, return which agent-authorable gaps are open: CLAUDE.md, test signal, lint config. (README/labels/CI are NOT auto-scaffolded — CI is off-limits `.forgejo/workflows/`.) - `scaffold_detect_stack` — Node + Eleventy (package.json present + `eleventy` in deps/scripts). Bail (empty) on anything else → ticket-only. - `scaffold_gen_claude_md` / `scaffold_gen_markdownlint` / `scaffold_add_test_script` — deterministic content from `package.json` (name/scripts) + README. Test signal for 11ty = a `\"test\": \"eleventy --dryrun\"`-style script (clean build is the gate). **Impure (guarded, integration):** - Idempotency: skip if `feat/agent-scaffold` branch or its PR already exists (don't re-open every tick). - Create the files on `feat/agent-scaffold` via the contents API (no clone), one PUT each; `package.json` is a read-jq-write update with its blob sha. - Open the scaffold PR assigned to `FORGEJO_REVIEWER`; **narrow the onboarding ticket** to \"agent scaffolded CLAUDE.md/test/lint in PR #N — only the CI workflow is left for you.\" **Acceptance:** a new unvalidated 11ty repo gets an auto scaffold PR matching scenekids #12 (minus CI); merging it validates the repo (if CI already exists) or leaves only \"add CI\"; idempotent; never auto-lands (human merge gate). Main live beneficiary: **parsley** (certifiedtradejobs is a slow-burn, not urgent). PR next.
Author
Collaborator

Done — implemented and merged in #311 (auto-scaffold: CLAUDE.md / test / lint on an idempotent, guarded, human-gated scaffold PR; onboarding ticket narrowed to the CI residual). Closing. I'll watch the next ticks for the scaffold PR to actually open on parsley and confirm the live behavior; if it doesn't fire, that resurfaces as its own signal.

Done — implemented and merged in **#311** (auto-scaffold: CLAUDE.md / test / lint on an idempotent, guarded, human-gated scaffold PR; onboarding ticket narrowed to the CI residual). Closing. I'll watch the next ticks for the scaffold PR to actually open on **parsley** and confirm the live behavior; if it doesn't fire, that resurfaces as its own signal.
igor closed this issue 2026-07-01 19:15:49 +00:00
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
joshtronic/igor#304
No description provided.