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:
- AI writes the code (Claude CLI orchestrating the appropriate
seken-*-engineerspecialist) - A different AI reviews the diff (Codex CLI cross-family review) and emits priority markers
- A deterministic gate reclassifies every PR from scratch, never trusts AI labels, and fails closed on ambiguity
- 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:
| Tier | Paths | Day-1 status |
|---|---|---|
always_human | finance, escrow, Xendit, auth middleware, mobile auth, secrets, .env* | π« permanent veto |
guardrails | .github/, scripts/autonomy/, agent specs, build config | π« permanent veto |
mobile_release_artifacts | iOS/Android signing, Info.plist, Fastlane | π« permanent veto |
low_migration | supabase/migrations/** | π locked until counter reaches threshold |
low_runtime_backend | backend/src/**/*.ts | π needs kill switch |
low_runtime_web | frontend/src/**/*.{ts,tsx,js,jsx} | π needs kill switch |
low_mobile_code | mobile/lib/**/*.dart | π |
low_ui_web | src/app/**/*.css, public/** | π |
docs_tests_lint | docs, 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:
| Tier | Counter threshold | Day-1 effect |
|---|---|---|
docs_tests_lint | 0 | unlocked at day 0 |
low_ui_web | 5 | locked until low_ui_web.clean_streak β₯ 5 |
low_mobile_code | 10 | locked until low_mobile_code.clean_streak β₯ 10 |
low_runtime_web | 10 | locked until low_runtime_web.clean_streak β₯ 10 |
low_runtime_backend | 10 | locked until low_runtime_backend.clean_streak β₯ 10 |
low_migration | 5 | locked 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.ymlafter 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:
-
Declare the flag as git commit trailers on the HEAD commit:
policy-gate.shreads 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> -
Reference an existing flag key in
.github/autonomy/runtime-flags.yml(human-maintained; AI cannot add keys) -
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 finding | Wrapper verdict | Behavior |
|---|---|---|
Any [P0] or [P1] | block + high | Push rejected; fix the issue and retry |
Any [P2] | human_gate + high | Push rejected; needs human review |
Only [P3] nits | approve + medium | Verdict committed; gate proceeds |
| No priority markers | approve + high | Verdict 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_feemust 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; noTRUNCATE XenditAdapterimport only insideapplication/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):
| Command | Effect |
|---|---|
/pause | sprint-loop state β paused |
/resume | sprint-loop state β running |
/stop | sprint-loop state β stop (loop exits next iteration) |
/status | reports sprint state + heartbeat age + loop ID |
/counters | dumps graduation tier counters |
/help | lists 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_gateuntil 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β
- Branch off
mainwith prefixfeat//fix//chore/ - Reference the Jira
PS-NNNkey in the branch name or the first commit - Make changes only in tiers that are unlocked for your developer
git push, the pre-push hook runs Codex (or Claude fallback) + the policy gate- Open a PR, apply the
autonomy-eligiblelabel - CI re-runs the gate
- Human reviews + merges
If you don't apply the label, the gate skips your PR entirely, normal review applies.
Repos that have the substrateβ
| Repo | Default base | Tiers populated |
|---|---|---|
pasarseken/pasarseken | develop, then main (two PRs from one branch) | all 9 |
pasarseken/SEKENDocs | main | docs_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.