Lewati ke konten utama

Autonomous Engineering

Pasar SEKEN runs an opt-in autonomous engineering pipeline that lets AI write, review, and gate code changes through deterministic safety checks before they reach develop or main. The system landed in May 2026 as v0.3 (substrate) + v0.4 (Telegram + sprint loop + agent specs).

This page documents what's live, what's gated, and what's still on the roadmap. The canonical spec lives in the brain repo at wiki/architecture/autonomous-engineering-v0.3.md.


What "autonomous" means here​

The system is not an unattended robot that merges its own work. It's a structured workflow where:

  1. AI writes the code (Claude CLI orchestrating the appropriate seken-*-engineer specialist)
  2. A different AI reviews the diff (Codex CLI cross-family review) and emits priority markers
  3. A deterministic gate reclassifies every PR from scratch, never trusts AI labels, and fails closed on ambiguity
  4. A human clicks Merge in v0.4. Auto-merge ships in v0.5.

The pipeline is opt-in per PR. PRs without the autonomy-eligible label do not enter the gate.


Pipeline (Signal β†’ Merge)​

Signal (Sentry / E2E / CI / human-filed Jira)
↓
pasarseken-brain-ai β†’ creates Jira ticket, classifies, proposes ai-ready (advisory)
↓
autonomous-writer β†’ delegates to seken-react-engineer / seken-backend-engineer / seken-flutter-engineer
↓
codex-check.sh β†’ Codex reviews; verdict derived from priority markers; commit lands at .autonomy/codex-verdict.txt
↓
Pre-push hook β†’ runs policy-gate.sh locally
↓
PR opened β†’ CI re-runs policy-gate.sh (no AI in CI)
↓
Human merge β†’ v0.4
↓
Canary β†’ serial concurrency lock (autonomous-canary.yml)

The risk tier system​

Every PR is classified against .github/ai-risk-tiers.yml into one of these tiers:

TierPathsDay-1 status
always_humanfinance, escrow, Xendit, auth middleware, mobile auth, secrets, .env*🚫 permanent veto
guardrails.github/, scripts/autonomy/, agent specs, build config🚫 permanent veto
mobile_release_artifactsiOS/Android signing, Info.plist, Fastlane🚫 permanent veto
low_migrationsupabase/migrations/**πŸ”’ locked until counter reaches threshold
low_runtime_backendbackend/src/**/*.tsπŸ”’ needs kill switch
low_runtime_webfrontend/src/**/*.{ts,tsx,js,jsx}πŸ”’ needs kill switch
low_mobile_codemobile/lib/**/*.dartπŸ”’
low_ui_websrc/app/**/*.css, public/**πŸ”’
docs_tests_lintdocs, wiki, *.md, tests, eslintβœ… unlocked at day 0

The PR's overall tier = the highest-precedence tier touched. Touching any veto tier blocks the PR regardless of how small the change is, humans land those PRs manually.


Graduation ladder​

Each tier carries its own clean_streak counter in .github/autonomy/counters.yml. The gate compares the PR's classified tier against that tier's own threshold:

TierCounter thresholdDay-1 effect
docs_tests_lint0unlocked at day 0
low_ui_web5locked until low_ui_web.clean_streak β‰₯ 5
low_mobile_code10locked until low_mobile_code.clean_streak β‰₯ 10
low_runtime_web10locked until low_runtime_web.clean_streak β‰₯ 10
low_runtime_backend10locked until low_runtime_backend.clean_streak β‰₯ 10
low_migration5locked until low_migration.clean_streak β‰₯ 5

v0.4 limitation: counters increment manually. A locked tier cannot get its first autonomous merge through the gate, its counter has to be bumped by hand in .github/autonomy/counters.yml after successful merges (autonomous or human) in that tier. v0.5 will add a post-merge workflow that auto-increments + a cross-tier dependency model so simpler-tier wins promote autonomy to higher tiers.

Reset triggers (manually applied today; automated v0.5): rollback within 24h, invariant breach, guardrail self-modification attempt, red-zone path touch, ignored Codex block, missing Jira key on a merged PR.


Kill-switch convention (runtime tiers)​

Any PR in low_runtime_* must:

  1. Declare the flag as git commit trailers on the HEAD commit: policy-gate.sh reads them from the last commit message, not from the PR body:

    feat(runtime): PS-NNN do the thing

    <body of the commit message…>

    Autonomy-Kill-Switch: <flag_key>
    Autonomy-Kill-Switch-Disable: <one-line procedure>
    Autonomy-Affected-Surfaces: <comma-separated routes/functions/screens>
  2. Reference an existing flag key in .github/autonomy/runtime-flags.yml (human-maintained; AI cannot add keys)

  3. Wire runtimeFlags.get('<key>') in the diff, the gate greps for the helper invocation

Flag helpers ship per surface:

  • Web: frontend/src/lib/runtime-flags.ts
  • Backend: backend/src/infrastructure/runtime-flags.ts
  • Mobile: mobile/lib/core/runtime/flags.dart

Disabling a flag rolls a deployed change back without git revert: set FLAG_<KEY>=false in the relevant env (Dokploy frontend env for web, Dokploy backend env for the NestJS service).


Codex review and verdict derivation​

The local codex-check.sh wrapper invokes Codex against the committed diff:

codex exec review --base <base-branch> -o /tmp/codex-review.md

Codex emits standard review output with priority markers ([P0], [P1], [P2], [P3]). The wrapper derives a verdict from those markers (no custom format required from Codex):

Codex findingWrapper verdictBehavior
Any [P0] or [P1]block + highPush rejected; fix the issue and retry
Any [P2]human_gate + highPush rejected; needs human review
Only [P3] nitsapprove + mediumVerdict committed; gate proceeds
No priority markersapprove + highVerdict committed; gate proceeds

On approve, the wrapper commits .autonomy/codex-verdict.txt:

AUTONOMY_CODEX_VERDICT=approve
AUTONOMY_CODEX_CONFIDENCE=high
AUTONOMY_CODEX_REVIEW_SHA=<sha of HEAD before this commit>
AUTONOMY_CODEX_REVIEWED_AT=<ISO8601 UTC>
AUTONOMY_CODEX_REVIEWER=codex

The verdict commit's parent is the reviewed code. The gate checks AUTONOMY_CODEX_REVIEW_SHA == HEAD~1; if any new commits land after the verdict, the SHA mismatches and the gate fails closed.

Fallback (Codex unavailable or the Engineer)​

When Codex isn't available, either temporarily down or because the developer's codex_available: false in .github/autonomy/developers.yml, the claude-review.sh script runs instead. Same artifact contract, with REVIEWER=claude-fallback.

Hard-enforced by the scripts (claude-review.sh and policy-gate.sh will fail-close on violation):

  • Tier ∈ {docs_tests_lint, low_ui_web}
  • ≀200 LOC changed
  • No supabase/migrations/**
  • No mobile/**

Guidance the fallback reviewer (Claude) is told to apply: but not deterministically enforced; Claude can still approve a violating PR if it overlooks the rule:

  • Single Jira key (no bundling)
  • No refactor-only commits

Treat the guidance-only items as soft expectations until v0.5 adds them to the deterministic gate.


Machine-checkable invariants​

12 deterministic rules under wiki/products/invariants.machine.yml in the brain (snapshot in pasarseken at .github/autonomy/invariants.machine.yml). Four reference the canonical register; eight derive from CLAUDE.md's "What Not to Do":

  • PAY-01 seller-side commission_fee must remain zero
  • AUTH-01 dual auth headers required on all authenticated API calls (Authorization + X-User-Token)
  • BRAND-01 stale palette hex values (old Terracotta, old Cream, purple range) forbidden in new code
  • RLS enabled on new tables; no DROP/ALTER POLICY; no TRUNCATE
  • XenditAdapter import only inside application/workers/**
  • Database credentials forbidden in client surfaces
  • OpenAI SDK forbidden in frontend / mobile
  • No hardcoded production database or API URLs in source code
  • VAPID keys must not be regenerated without subscription migration

The gate runs each rule as a grep against added diff lines within the rule's scope. Any action: block rule matching fails the PR.


Telegram control bot​

The bot @seken_ai_engineer_bot runs continuously via macOS launchd on the developer's machine. Commands (the Founder's chat only, other chats are silently ignored):

CommandEffect
/pausesprint-loop state β†’ paused
/resumesprint-loop state β†’ running
/stopsprint-loop state β†’ stop (loop exits next iteration)
/statusreports sprint state + heartbeat age + loop ID
/countersdumps graduation tier counters
/helplists commands

The bot also receives push notifications from notify.sh for six event types: phase_landed, gate_passed, gate_failed, canary_engaged, canary_failed, heartbeat_silent.


What's deferred to v0.5​

  • Auto-merge on green gate, humans currently click Merge
  • Active canary monitoring: the workflow holds the concurrency lock but doesn't poll Sentry/Vercel/Supabase yet
  • Counter auto-increment workflow, counters are bumped manually after each clean merge
  • Mobile remote-config: mobile flags are compile-time; disabling requires a new build
  • Migration shadow DB: migrations land as human_gate until Supabase preview-branch integration ships
  • brain-ai signal ingestion: Sentry/E2E/CI webhooks not yet wired; humans file tickets in the interim
  • Dashboard data sync: the dashboard at ai-engineer.pasarseken.id currently shows fallback sample data; the localβ†’hosted JSON sync ships in v0.5

How to opt a PR into the gate​

  1. Branch off main with prefix feat/ / fix/ / chore/
  2. Reference the Jira PS-NNN key in the branch name or the first commit
  3. Make changes only in tiers that are unlocked for your developer
  4. git push, the pre-push hook runs Codex (or Claude fallback) + the policy gate
  5. Open a PR, apply the autonomy-eligible label
  6. CI re-runs the gate
  7. Human reviews + merges

If you don't apply the label, the gate skips your PR entirely, normal review applies.


Repos that have the substrate​

RepoDefault baseTiers populated
pasarseken/pasarsekendevelop, then main (two PRs from one branch)all 9
pasarseken/SEKENDocsmaindocs_tests_lint, low_ui_web, low_runtime_web, guardrails, always_human

Future repos can install the substrate by copying scripts/autonomy/, .husky/pre-push, .github/ai-risk-tiers.yml + .github/autonomy/, and the three .github/workflows/autonomous-*.yml workflows.


Why this exists​

Pasar SEKEN is a two-person engineering org. The autonomy system multiplies the rate at which low-risk work clears the backlog (docs, UI tweaks, small bugfixes) while keeping irreversible work, money flow, auth, mobile signing, strictly human. Deterministic gates plus cross-family AI review (Claude writes, Codex reviews) catch the failures one LLM family wouldn't catch on its own.

The system is fail-closed by design: any ambiguity, missing artifact, or unmatched invariant blocks the PR. Speed comes from a clean codebase plus a small invariant set, not from looser checks.