Skip to content

리랜딩: 피드백 자동 트리아지 스킬 (Plan B) — 거버넌스 정합 게이트 - #8

Merged
SSFSKIM merged 2 commits into
mainfrom
feat/triaging-feedback-reland
Jul 9, 2026
Merged

리랜딩: 피드백 자동 트리아지 스킬 (Plan B) — 거버넌스 정합 게이트#8
SSFSKIM merged 2 commits into
mainfrom
feat/triaging-feedback-reland

Conversation

@SSFSKIM

@SSFSKIM SSFSKIM commented Jul 9, 2026

Copy link
Copy Markdown
Owner

무엇

피드백 자동 트리아지 스킬(skills/triaging-feedback, Plan B)의 리랜딩 PR. 내용은 PR #7에서 리뷰·머지됐던 최종 상태와 비트 단위 동일(스킬 서브트리 4834856·스펙 blob 4c5fb00 해시 일치 확인).

왜 다시 PR인가 — 이력

  1. PR feat(triaging-feedback): 피드백 자동 트리아지 스킬(Plan B) — Codex-SDK 워커·게이트·needs-human 티켓 #7 머지(81a8c19) — 태스크별 리뷰 + Opus 전체-브랜치 리뷰 + 머지 전 외부 3자 리뷰(Codex/gpt-5.5, Critical 2 포함 F1–F7 수정) 통과.
  2. main의 동시 워커 거버넌스 변경(79e063a·058a475 등)과의 정합(reconcile) 확인을 위해 사람 요청으로 리버트(185ad75).
  3. 다른 세션이 Reapply(aad1bf3)를 main에 직접 랜딩 — 실수로 확인됨 → 재리버트(d7ceb6a).
  4. 이 PR = d7ceb6a의 리버트(리랜딩) — 거버넌스 정합 확인 후 사람 리뷰·머지 게이트를 거치기 위함.

머지 전 확인할 것

  • 워커 거버넌스 정책(구현 워커 자율성 7.10.0 + 79e063a 리뷰어 Standards + 058a475 fork park/exit 조항)과 이 스킬의 디스패치·티켓·게이트 정책이 상충하지 않는지
  • 상충 시: 이 브랜치에 정합 커밋 추가 후 머지

리뷰 근거·검증 기록은 PR #7 본문과 코멘트(외부 리뷰 findings 표) 참조.

구현과 SKILL.md는 처음부터 needs-human을 쓰는데(board-register.sh --state
needs-human) 스펙 본문 5곳만 구 어휘로 남아 있었다. v8 who-unparks 판별식
기준으로도 정정이 맞다: 트리아지 티켓은 인간 본인의 제품/우선순위 결정을
기다린다 — 스펙의 괄호 정의 자체가 이미 needs-human의 v8 정의였다.
Revision Notes에 드리프트 기록 추가.
@SSFSKIM
SSFSKIM merged commit dbf48d9 into main Jul 9, 2026
@SSFSKIM
SSFSKIM deleted the feat/triaging-feedback-reland branch July 9, 2026 23:07
SSFSKIM added a commit that referenced this pull request Jul 10, 2026
Two-tier trust (review #1, human design call): developer feedback — the
server-resolved role snapshot in TRIAGE_TRUSTED_ROLES (default admin), or
a stripped .env #TRIAGE_DEV_CODE body prefix — is read as instruction
(R1/R4 waived: dev ideas can be born ready-for-agent, labeled
source:dev-feedback); user feedback keeps the conservative gate, residual
risk explicitly accepted in the Decision Log. The dev code is stripped
before the body reaches the prompt or ticket so it never leaks into a
public issue.

The rest of the review, all fixed:
- #2 R2 requires a REAL file citation (path-shaped after :line strip;
  unknown:12 no longer counts)
- #3 R3 scans risk SYMBOLS (assertStudentAccess, supabaseAdmin, RLS,
  generate-plan layout fns, past_exam_problems, SUPABASE_SERVICE_ROLE_KEY)
  in addition to paths
- #4 claim issues a lease token; writeback is lease-conditional (late
  writeback after reclaim throws instead of clobbering); reclaim window
  validated at config load; second idempotency check right before
  registerTicket
- #5 findExisting fails closed on gh search errors
- #6 ticket body temp files: mkdtemp dir, mode 0600, removed in finally
- #7 TRIAGE_ENABLED checked before config parsing
- #8 spec drift fixed (feedback-dispatch.ts references, two-kill-switches)

p86 DDL note gains triage_lease UUID. New src/trust.ts. 91 tests green,
tsc clean.
SSFSKIM added a commit that referenced this pull request Jul 12, 2026
… soul, seeding contract

Runnable form of the steals doc §2 (state/compute separation):

- infra/worker-host/cloud-init.yaml — disposable body for a CX43/Ubuntu 24.04
  host: blank-only volume format + /data mount before user creation, worker
  user (fixed uid, home ON the volume, no sudo), Node LTS, gh, codex, claude
  (native installer as worker), tailscale, swap. No Docker by design (host-
  process registry model, codex Landlock on a real kernel, cloud-init IS the
  reproducibility).
- infra/worker-host/env.example — seeding contract: two-token scope split
  (board token in env, push token only in clone remotes), setup-token /
  auth.json paths, SSL_CERT_FILE pin.
- infra/worker-host/README.md — layer split, hcloud creation, seeding
  checklist, Linux verification gate (dogfood cell), body-rebuild drill.
- _codex_launch + review-engine.sh: SSL_CERT_FILE probe now also covers
  /etc/ssl/certs/ca-certificates.crt — TECH-DEBT #8 RESOLVED.

Suites: codex-scripts, review-engine, shell lint — green; cloud-init YAML
syntax-checked.
SSFSKIM added a commit that referenced this pull request Jul 12, 2026
…iveness (managed-agents steals) (#10)

* feat(orchestrating-daemons): host-stamped registry + host-aware pid liveness — managed-agents steals

Adopted from Anthropic's 'Scaling Managed Agents: Decoupling the brain from
the hands' (transfer analysis in docs/doperpowers/2026-07-12-managed-agents-steals.md):

- Every registration/resume stamps `host` (DAEMON_HOST, default hostname)
  into daemon metas; _pid_alive() in _lib.sh treats a foreign-host pid as
  dead regardless of kill -0 (a registry migrated on a state volume carries
  pid numbers, not processes). Empty host = legacy meta = local.
- All four pid consumers routed through host-aware checks: codex-resume
  one-turn guard, daemon-retire kill, review/land-dispatch _is_live dedupe,
  review-dispatch _wt_occupied worktree-removal guard.
- Steals doc also records the state-volume convention (steal 2) and the
  token-wired-remote scope-split recipe (steal 3, narrows TECH-DEBT
  accepted note on GH_TOKEN in worker env — pointer added).
- Tests: foreign-host pid → resume proceeds + re-stamps, retire leaves the
  unrelated process alone, dispatchers retire+respawn; same-host controls
  unchanged. All 5 suites + shell lint green.

* feat(infra): worker-host provisioning — cloud-init body, state-volume soul, seeding contract

Runnable form of the steals doc §2 (state/compute separation):

- infra/worker-host/cloud-init.yaml — disposable body for a CX43/Ubuntu 24.04
  host: blank-only volume format + /data mount before user creation, worker
  user (fixed uid, home ON the volume, no sudo), Node LTS, gh, codex, claude
  (native installer as worker), tailscale, swap. No Docker by design (host-
  process registry model, codex Landlock on a real kernel, cloud-init IS the
  reproducibility).
- infra/worker-host/env.example — seeding contract: two-token scope split
  (board token in env, push token only in clone remotes), setup-token /
  auth.json paths, SSL_CERT_FILE pin.
- infra/worker-host/README.md — layer split, hcloud creation, seeding
  checklist, Linux verification gate (dogfood cell), body-rebuild drill.
- _codex_launch + review-engine.sh: SSL_CERT_FILE probe now also covers
  /etc/ssl/certs/ca-certificates.crt — TECH-DEBT #8 RESOLVED.

Suites: codex-scripts, review-engine, shell lint — green; cloud-init YAML
syntax-checked.

* feat(infra): triage tenant layer — separate uid, systemd timer, seeding contract

First production tenant of the worker host: the triaging-feedback poller
moves off the Mac's launchd onto the VM as its burn-in workload.

- cloud-init: triage user (uid 1011, /data/triage — separate from worker
  because its env holds SUPABASE_SERVICE_ROLE_KEY while worker executes
  PR-derived code); doper-triage systemd oneshot+timer replacing the
  launchd label (same single-instance serialization the reclaim design
  needs); ExecCondition on the seeded .env makes pre-seed ticks silent
  no-ops and rebuilds zero-touch.
- env.triage.example: skill-dir .env contract — VM deltas vs Mac (explicit
  GH_TOKEN issues-scope since no keychain; contents-READ-only PAT in the
  ida-solution remote; triage never pushes).
- README: §5 tenant seeding + one-tick verification + Mac handoff; rebuild
  drill and stale feedback-intake bullet updated.

* fix(infra): worker-host review findings — per-body hostname, runner env/label, honest PAT boundary

Five findings from PR #10 review, all confirmed against the code:

- Static hostname doper-worker-1 broke host-aware liveness on the rebuild
  path (_lib.sh stamps $(hostname); same name = old metas read local, a
  recycled pid could alias an unrelated process). runcmd now sets
  doper-worker-$(machine-id prefix) — unique per body since machine-id is
  minted at first boot — with preserve_hostname guarding per-boot reverts.
- Runner registration label doper-worker never matched the shipped
  workflow's runs-on [self-hosted, claude-review]; jobs would queue forever.
- Runner jobs read ~/runner/.env + .path, not .bashrc (non-login; Ubuntu
  .bashrc early-returns non-interactive). §2.6 now seeds both; the
  cloud-init comment claiming bash -lc coverage is corrected.
- Rebuild drill said 'restart the runner service' but the svc.sh systemd
  unit lived on the discarded root fs — drill now reinstalls it.
- README claimed the push PAT never enters worker reach; any worker-uid
  process can read it via git remote get-url. Text now states the honest
  boundary (workers ARE contents-write principals) and requires branch
  protection before first run.

* fix(infra): /data mount hard gate + codex-auth seeding path

Second review wave, both findings confirmed:

- nofail (kept: a missing volume must degrade to a reachable system, not
  systemd emergency mode) let provisioning 'succeed' with /data on the
  disposable root fs — the users module creates the homedirs there before
  runcmd. runcmd now opens with a mountpoint assertion that aborts loudly;
  final_message and README §1 direct to 'cloud-init status --long' before
  seeding instead of claiming readiness.
- README §2.2's scp to worker@host always failed: worker has a locked
  password, no authorized keys, and no ~/.codex. Documented the real path —
  through the login account, install(1) with owner/mode set from birth,
  no temp-file exposure.

* fix(infra): tenant home hardening, git identity seeding, fail-fast provisioning

Third review wave, all three confirmed:

- The uid boundary relied on the image's login.defs default home mode;
  anything group/other-traversable lets triage read worker's clones (push
  PAT in each .git/config). runcmd now chmods both homes 0700 (idempotent
  against an existing soul), and the runner .env recipe adds chmod 600 —
  it carries the same tokens as ~/.env but was born under default umask.
- A fresh soul has no git identity and git refuses to auto-detect email
  from the machine-id hostname, so a worker's first commit dies with
  'Author identity unknown'. Seeding §2.4 now sets user.name/user.email
  once per soul (~/.gitconfig rides the volume).
- runcmd merged into one sh script with no set -e meant a failed
  NodeSource/Tailscale/claude installer still yielded 'cloud-init status:
  done'; curl|bash additionally masked download failures. First item is
  now set -e (governs the whole merged script), curl|bash split into
  download-then-execute (sh is dash — no pipefail; the claude installer
  keeps the pipe but under an inner bash with pipefail), and provisioning
  ends with an explicit binary/Node-major/claude-on-worker-PATH gate.

* fix(orchestrating-daemons): bind liveness to host boot

* fix(orchestrating-daemons,infra): strip RUNNER_TRACKING_ID at detach; triage verify from admin shell

Fourth review wave, both findings confirmed:

- Detached workers spawned from an Actions dispatch job inherited
  RUNNER_TRACKING_ID, and the runner's post-job cleanup kills any surviving
  process whose environ carries that marker — nohup/--bg detach the session,
  not the env, so the primary automated dispatch path reaped its own workers
  at job exit (stale 'working' meta left behind). All three detach points
  (daemon-spawn, daemon-resume fork, _codex_launch wrapper) now spawn under
  env -u RUNNER_TRACKING_ID — a no-op outside Actions. README §2.6 names
  this as the mechanism instead of implying nohup suffices.
- README §5.4 had the first-tick verification run 'sudo systemctl' inside
  the sudo -iu triage shell — triage has no sudo, a locked password, and no
  journal access. Verification now runs from the admin shell; §5 intro marks
  step 4 as the exception.

Suites: test-daemon-scripts, test-codex-scripts, test-review-dispatch all
pass; shell lint clean.

* fix(infra): codex-auth seeding — GNU install refuses pipe-backed /dev/stdin

Fifth review wave, confirmed empirically (GNU coreutils install exits 1
with 'skipping file /dev/stdin, as it was replaced while being copied' and
creates nothing): the §2.2 recipe could never seed auth.json on the Ubuntu
target. Replaced with umask 077 + cat (file born 0600, still no exposure
window) followed by chown to worker.

* fix(orchestrating-daemons): register resumed turn early

* fix(orchestrating-daemons): gate superseded-turn stop/purge on recorded host identity

A migrated meta's short is host-local and reusable — stop/rm through it can
hit an unrelated local agent. Capture the identity verdict before the fork
re-stamps the meta; foreign metas skip both (leftover old-host session files
are cosmetic, a purge through a reused short is destructive).

* fix(orchestrating-daemons): parse the LEADING sec field of kern.boottime

The greedy .*sec-= match landed inside 'usec = ' and recorded microseconds
as the boot identity — a value that can repeat across reboots, defeating
the host-boot liveness gate on macOS. Anchor on the leading '{ sec = N,'.
Regression cross-checks _boot_id against an independent parse.

env.triage.example: header marks the file fork-local (names the fork's real
consumer repo on purpose; never upstream) — review finding triaged as void
for a personal fork, kernel kept as a permanent guard note.
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