Skip to content

esaldgut/lessongate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lessongate

A local Go runtime agent that keeps a public engineering-knowledge repository dynamic: it watches a private project's merged pull requests, asks Claude which of the lessons captured from them are generalizable (reusable patterns, not business-specific), sanitizes them through an NDA gate, and opens draft pull requests to a public skills repo for human review.

It automates an upstream that would otherwise be manual, and it is built to a production bar: every stage is behind an interface, the security-critical gate is deterministic and tested against a golden corpus, and the whole pipeline runs deterministically offline in CI.

            private repo (merged PRs + a curated lesson registry)
                                    │
   ┌────────────────────────────────┼────────────────────────────────────────┐
   │ lessongate (single-process Go binary, single-run flock)                 │
   │                                                                         │
   │  WATCH ─▶ PREFILTER ─▶ REDACT ─▶ EXTRACT ─▶ GATE ─▶ RECONCILE ─▶ EMIT   │
   │  go-github  (no API)   (det.,    (Claude:   (det.   (novel/     (draft  │
   │  mergedAt              before    is it      regex + overlaps/   PR,     │
   │  cursor               any API)   general-   corpus  duplicate)  idempo- │
   │                                  izable?)   + canary           tent)    │
   │                                  + Claude                               │
   │                                  verify                                 │
   │  ledger (JSONL, atomic, fail-closed) · slog (redacted) · core dumps off │
   └──────────────────────────────────────────────────────────────────────┬──┘
                                                                          │
                                            DRAFT PR (fingerprinted) ─────┘
                                                      │
                            public repo (branch-protected) ─▶ human review

Why it's interesting

  • The trust boundary is on the right side of the wire. An early design sent the raw PR diff to the Claude API before sanitizing. That ships every private identifier to a third party on every PR. The fix: consume the human-curated lesson text (already distilled to a pattern, low NDA density) instead of the raw diff, and run a deterministic redaction pass before anything leaves the process. The diff never leaves the machine.
  • The confidentiality control is a deterministic gate, not an LLM. A blacklist of literals is guaranteed-incomplete. The primary control is structural regex (12-digit account IDs, ARNs, reverse-DNS bundle IDs, secret-shaped tokens) plus a template allow-list, tested against a golden corpus of leaky/clean fixtures with a seeded canary that fails the build if it ever leaks. The Claude verify pass is extra recall — it can only downgrade a verdict to unsafe, never the reverse.
  • Determinism where it counts. The non-deterministic Claude stages run against recorded cassettes in CI, so the security-critical path is regression-tested offline. The deterministic gate, the merge-cursor logic, the ledger's crash-resume, and the idempotency check are all covered by fast unit tests.
  • It reuses, rather than reinvents. Skill validation shells out to the official Claude Code skill-creator plugin's quick_validate.py, discovered by glob (no hardcoded version hash) with a fail-closed startup assert.

Design decisions, verified not assumed

The dependency stack was checked against live sources, which overturned several assumptions:

Decision Why
anthropic-sdk-go v1.46.0 Current. Opus 4.8 is adaptive-thinking-onlytemperature/top_p/budget_tokens all 400, so determinism comes from forced structured output (tool-choice), not a temperature knob.
go-github v88 Current. NewClient returns (*Client, error) and auth is a ClientOptionsFunc (WithAuthToken) — a breaking change from older majors. Draft PRs via NewPullRequest{Draft: true}.
No MCP go-sdk Real, but speculative here — the agent calls the API and GitHub directly; there is no MCP server in the data flow.
No OpenTelemetry Over-engineering for a single-process local binary. slog + counters instead.
Go 1.26 idioms errors.AsType[T], wg.Go, strings.SplitSeq, omitzero — used where they fit.

The design was put through an adversarial audit before implementation. The two findings tagged CRITICAL — raw diffs crossing the wire before the gate and a blacklist as the primary confidentiality control — changed the architecture above, not a footnote.

Threat model (summary)

Boundary What crosses Control
process → Claude API redacted lesson text (not raw diff) deterministic redaction before send; ZDR key required
process → public repo SKILL.md + PR body full gate (det + structural + allow-list + Claude verify + canary) before; draft-only; human review; server-side secret scan
process → disk quarantine (if any) outside synced paths, chmod 700, TTL-shred, no core dumps
process → logs telemetry slog redactor — IDs/hashes/counts only, never payloads
GitHub token credential fine-grained PAT, 2 repos, Keychain, not inherited by subprocesses

See docs/RUNBOOK.md for the pre-flight checklist and the leak-response procedure.

Layout

cmd/lessongate/        CLI: run / backfill / dry-run / status
internal/
  watch/               go-github merged-PR cursor (mergedAt, not PR number)
  prefilter/           drop docs-only / dep-bump / own capture commits, no API
  lessons/             parse the curated lesson registry (1:N, fenced-code safe)
  redact/              deterministic pre-flight: deny-list + structural + allow-list
  extract/             Claude: is this lesson generalizable?
  gate/                deterministic gate + golden corpus + canary + Claude verify
  reconcile/           novel / overlaps-existing / duplicate
  skillcreator/        shell-out to the skill-creator plugin (glob discovery, fail-closed)
  emit/                idempotent draft PR via go-github (fingerprint in body)
  claude/              anthropic-sdk-go wrapper + cassette client for offline CI
  ledger/              JSONL state, atomic writes, flock, fail-closed
  obs/                 redacting slog handler + counters
  pipeline/            the orchestrator wiring the six stages

Running it

go build ./... && go test ./...          # 68 tests, all offline

export ANTHROPIC_API_KEY=...             # must be a ZDR-enabled key
export LESSONGATE_GITHUB_TOKEN=...        # fine-grained PAT, 2 repos

./bin/lessongate dry-run --repo owner/name   # list candidates, open NO PRs
./bin/lessongate run --repo owner/name --max 1   # open one draft PR
./bin/lessongate run --repo owner/name --max 1   # re-run: idempotent, no duplicate

Without credentials the agent fails closed with a clear message — it never degrades to a silent no-op that would skip the gate.

Status

v0.1 — the full pipeline is wired and tested offline (73 tests across 13 packages, deterministic CI). The agent is complete; what gates the first production run is a deliberate precondition, not missing work.

Processing real lessons from a private project means sending redacted lesson text to a third-party API. The threat model requires Zero-Data-Retention on the Anthropic organization before that happens. ZDR is not a free-tier toggle — it is granted under a commercial agreement (see docs/RUNBOOK.md). So lessongate waits for ZDR rather than processing private data without it. The agent enforces its own threat model; it doesn't get bypassed because the author owns the data.

Until then the synthetic smoke path exercises the entire pipeline — real Claude API calls, a real draft PR — on lesson text that carries no private project data:

./bin/lessongate dry-run --once --lessons-file testdata/smoke/synthetic_lessons.md

Non-goals for v0.1: MCP, OpenTelemetry, auto-merge, and watching anything but the single private vertical slice.

About

Local Go runtime agent that watches a private repo's merged PRs, detects generalizable engineering lessons with Claude, sanitizes them through an NDA gate, and opens draft PRs to a public skills library. The agent that keeps ai-native-engineering-workspace dynamic.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages