Skip to content

build: SimplyCubed Code foundation (gate-first Go core) - #2

Merged
charlesgreen merged 8 commits into
mainfrom
build/foundation
Jul 29, 2026
Merged

charlesgreen merged 8 commits into
mainfrom
build/foundation

Conversation

@charlesgreen

@charlesgreen charlesgreen commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Overnight foundation pass for SimplyCubed Code. Built engine-first so the whole loop is testable with zero LLM spend. Eight commits, each keeping make check green (verified per-commit in an isolated worktree), meant to be reviewed commit by commit.

What's here (M1–M7, all green)

  • Gate + CI — Makefile check (gofmt + vet + build + test), run by .github/workflows/check.yml.
  • internal/domain — core types, no model/GitHub dependency.
  • internal/engine — the Runner interface (the one seam to a model) + a deterministic fake that drives every test.
  • internal/gate — runs the repo's gate command; captures exit, output tail, and a normalized stall signature.
  • internal/config — loads .github/simplycubed.yml; refuses a config with no gate:.
  • internal/state — the sc: label lifecycle with mutual-exclusion transitions.
  • internal/loop — goal -> act -> grade -> repeat; opens a PR on success, escalates (Blocked, no PR) on a stall. Includes the honesty test.
  • internal/forge — GitHub side as an interface with no merge method, plus a recording fake.
  • internal/ledger — append-only JSONL run events, wired into the loop.
  • internal/worktree — idempotent per-issue git worktree isolation.
  • docs/spec/architecture.md + six ADRs in docs/decisions/.

The code-vs-docs line (deliberate)

Engine-dependent design (role prompts, verdict schema, budget/stall numbers, Action packaging) is written as ADRs, not coded, because the blocked S3 Azure replay is what validates those assumptions. See STATUS.md and loop/goal.json.

Left for you (in STATUS.md)

  • make check is not a required status check yet (avoids a solo self-approval deadlock).
  • No repo settings changed overnight.
  • CI actions pinned by tag, not SHA yet.

Blocked on you (not faked)

The Phase 0 spikes (S1 Azure auth, S3 replay) need the Azure resource name + gpt-5.4 / gpt-5.4-mini deployment names and AZURE_OPENAI_API_KEY. codex CLI is installed (0.146.0); an isolated CODEX_HOME template is staged in scratch so your ~/.codex is untouched.

No secrets or private material in the diff (grepped before every push, and the full diff re-checked).

Gate-first scaffolding for the Go core.

- Makefile `check` = gofmt + go vet + build + test. This is the gate.
- .github/workflows/check.yml runs it in CI (not a required check yet; that
  is a maintainer decision, see STATUS.md).
- cmd/simplycubed: minimal CLI (`version`), the local-run entry point.
- internal/buildinfo: build-time version, with a test so the gate has teeth.
- loop/goal.json: the machine-readable goal, invariants, and milestones for
  this build, dated 2026-07-30.
- STATUS.md: honest handoff — what exists, what is deferred to a maintainer,
  and what is blocked on Azure access.
- docs/spec/architecture.md: the system shape.

Engine-independent by design; engine-dependent decisions are documented, not
coded, pending the Azure replay spike.
The engine-independent core.

- internal/domain: plain types (Issue, RunRequest, RunResult, Verdict,
  Finding) with no model or GitHub dependency, plus Verdict.HasBlocker so the
  loop never trusts a "pass" that carries a blocker finding.
- internal/engine: the Runner interface, the single seam to a model. The loop
  depends only on this.
- internal/engine/fake: a deterministic scripted Runner. No network, no spend.
  This is what makes the whole loop, including its failure paths, testable.

Tests cover the fake's ordering, no-op-past-end, and engine-error paths.
- internal/gate: runs the repo's gate command, captures exit code and a bounded
  output tail, and computes a normalized signature so the loop can distinguish a
  stall (same failure again) from progress (a different failure). Signature
  strips ANSI, durations, hex, and bare numbers.
- internal/config: loads .github/simplycubed.yml and refuses a config with no
  `gate:` (an ungated loop is not built). Stdlib-only flat-key parser for now,
  documented as a bootstrap to be swapped for a real YAML parser.
- internal/state: the sc: label lifecycle with mutual-exclusion transitions.
  Go is the only human-applied state; Blocked and Done are terminal.

All green; tests cover pass/fail/signature, the missing-gate refusal, and the
legal/illegal transition sets.
The centerpiece: goal -> act -> grade -> repeat, ending exactly two ways.

- internal/forge: the GitHub side as an interface (OpenPR, SetState, Comment).
  Note the absence of a Merge method: the loop cannot merge, by construction.
- internal/forge/fake: a recording forge for tests.
- internal/loop: the engine. It grades each round against an injected gate,
  feeds a red gate back into another round, and stops when the gate passes
  (open a PR) or when it stalls (two reds with the same signature -> escalate,
  open NO PR). MaxRounds is a backstop. Prompt assembly is left as a placeholder
  with a pointer to the ADR, per the code-vs-docs boundary.

Tests: success on round 1, fix on round 2, engine error -> blocked/no-PR, and
the honesty test -- an engine that never fixes the gate ends Blocked and opens
zero pull requests. A loop only ever seen to succeed is no loop at all.
Six ADRs capturing the decisions from design, with the engine-dependent ones
marked pending the S3 replay spike so they are recorded but not coded against a
guess:

- 0001 engine-independent core (implemented)
- 0002 no merge path in the binary (implemented, structural)
- 0003 Codex on Azure as the first engine adapter (adapter pending S3)
- 0004 role prompts and the reviewer verdict (pending S3)
- 0005 budgets and stall thresholds (pending S3; numbers come from real runs)
- 0006 runtime on GitHub Actions, and identity (one-App-vs-two open)
- internal/ledger: append-only JSON-line events (a line per round with the gate
  result, a terminal line per run with the outcome). Clock is injectable for
  deterministic tests. This is the audit trail and the basis for later cost and
  status reporting; it is engine-independent.
- loop: optionally records a round line each iteration and a run_end line at the
  terminal outcome. Nil ledger is a no-op, so it stays out of the way in tests
  that do not need it.

Tests cover the JSONL shape (one line per event, zero fields omitted) and the
loop emitting fail/pass round lines plus a pr_opened run_end.
- internal/worktree: create, list, and remove a git worktree per issue so
  concurrent loops never share a checkout. Add is idempotent -- an existing
  worktree for a branch is detected and reused rather than erroring, since git
  refuses to check the same branch out twice. Tested end to end against a
  temporary git repo (init, commit, add, list, re-add, remove).
- STATUS: record it.
@charlesgreen
charlesgreen merged commit 7696e63 into main Jul 29, 2026
1 check passed
@charlesgreen
charlesgreen deleted the build/foundation branch July 29, 2026 22:46
charlesgreen added a commit that referenced this pull request Sep 19, 2026
* fix(gemini): trust workspace for headless gemini-cli

gemini-cli 0.60+ exits 55 (FatalUntrustedWorkspaceError) when folder
trust is on and the Actions checkout is not in trustedFolders.json.
-y auto-approves tools but does not bypass that gate.

Set GEMINI_CLI_TRUST_WORKSPACE=true by default (ExtraEnv can override)
and include a short CLI output tail in engine errors so bot comments
show the real failure next time.

Unblocks Sync #1/#2 and code#147 dogfood after Vertex preflight.

* test(gemini): cover silent CLI failure error path

Codecov patch flagged the empty-output branch of the exit-55
error formatting. Exercise a non-zero exit with no stdout/stderr.
@charlesgreen charlesgreen mentioned this pull request Sep 19, 2026
2 of 3 tasks
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