@kaydash9999/banker-plugins 0.22.0 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +68 -0
  4. package/PRIVACY.md +1 -0
  5. package/README.md +39 -13
  6. package/bin/banker.js +118 -22
  7. package/bin/lib/codex-hooks.mjs +116 -18
  8. package/bin/lib/codex-names.mjs +4 -0
  9. package/bin/lib/codex-routes.mjs +93 -1
  10. package/bin/lib/command-skill.cjs +37 -6
  11. package/codex/command-skills/front-qa/SKILL.md +1 -1
  12. package/codex/command-skills/setup/SKILL.md +4 -4
  13. package/codex/hooks.json +5 -5
  14. package/codex/manifest.json +18 -15
  15. package/codex/skills/progress/scripts/progress.mjs +44 -14
  16. package/codex/skills/teamwork/SKILL.md +143 -0
  17. package/codex/skills/teamwork/scripts/teamwork.mjs +1007 -0
  18. package/codex/transform-matrix.md +5 -3
  19. package/hooks/obsidize-hook.mjs +75 -32
  20. package/hooks/telemetry-count-codex.mjs +238 -57
  21. package/hooks/update-fetch.mjs +2 -1
  22. package/hooks/update-notify.mjs +82 -20
  23. package/package.json +3 -1
  24. package/skills/3d-intro-build/SKILL.md +101 -20
  25. package/skills/3d-intro-build/references/NOTICE.md +9 -2
  26. package/skills/3d-intro-build/references/assemble.mjs +113 -22
  27. package/skills/3d-intro-build/references/azure-adapter.mjs +110 -24
  28. package/skills/3d-intro-build/references/clip-post.mjs +443 -0
  29. package/skills/3d-intro-build/references/dive-mode.mjs +299 -0
  30. package/skills/3d-intro-build/references/intro-fixes.css +11 -0
  31. package/skills/3d-intro-build/references/prompts.md +51 -4
  32. package/skills/3d-intro-build/references/scrub-engine.js +1 -0
  33. package/skills/3d-intro-build/references/step-nav.css +29 -0
  34. package/skills/3d-intro-build/references/step-nav.js +284 -106
  35. package/skills/3d-intro-build/references/verify-intro.mjs +373 -32
  36. package/skills/3d-intro-build/references/verify.md +6 -0
  37. package/skills/3d-intro-build/references/video-pool.mjs +142 -12
  38. package/skills/3d-intro-setup/SKILL.md +20 -7
  39. package/skills/3d-intro-setup/references/azure-adapter.mjs +110 -24
  40. package/skills/3d-intro-setup/references/video-pool.mjs +142 -12
  41. package/skills/all-in-one/SKILL.md +125 -86
  42. package/skills/omc-patch/SKILL.md +1 -1
  43. package/skills/payload-mon/SKILL.md +1 -1
  44. package/skills/play-qa/SKILL.md +6 -4
  45. package/skills/progress-mon/README.md +164 -0
  46. package/skills/progress-mon/SKILL.md +165 -0
  47. package/skills/progress-mon/assets/fixture/README.md +1 -0
  48. package/skills/progress-mon/assets/fixture/arch.json +33 -0
  49. package/skills/progress-mon/assets/fixture/commits.json +7 -0
  50. package/skills/progress-mon/assets/fixture/config.json +30 -0
  51. package/skills/progress-mon/assets/fixture/events.jsonl +7 -0
  52. package/skills/progress-mon/assets/fixture/flow.json +44 -0
  53. package/skills/progress-mon/assets/fixture/items.json +14 -0
  54. package/skills/progress-mon/assets/fixture/req-spec.json +31 -0
  55. package/skills/progress-mon/assets/fixture/reqs.json +11 -0
  56. package/skills/progress-mon/assets/fixture/variants/arch-auto/arch.json +58 -0
  57. package/skills/progress-mon/assets/fixture/variants/check-auto/arch.json +15 -0
  58. package/skills/progress-mon/assets/fixture/variants/check-overlap/arch.json +14 -0
  59. package/skills/progress-mon/assets/fixture/variants/history-manual/config.json +13 -0
  60. package/skills/progress-mon/assets/fixture/variants/history-manual/items.json +7 -0
  61. package/skills/progress-mon/assets/fixture/variants/workflow-wide/flow.json +35 -0
  62. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a01.jsonl +1 -0
  63. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a02.jsonl +1 -0
  64. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a03.jsonl +1 -0
  65. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a04.jsonl +1 -0
  66. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/journal.jsonl +7 -0
  67. package/skills/progress-mon/references/arch.md +280 -0
  68. package/skills/progress-mon/references/authoring.md +199 -0
  69. package/skills/progress-mon/references/config.md +115 -0
  70. package/skills/progress-mon/references/flow.md +75 -0
  71. package/skills/progress-mon/references/inventory.md +58 -0
  72. package/skills/progress-mon/references/items.md +74 -0
  73. package/skills/progress-mon/references/req-spec.md +74 -0
  74. package/skills/progress-mon/references/reqs.md +30 -0
  75. package/skills/progress-mon/scripts/index.js +31 -0
  76. package/skills/progress-mon/scripts/pm-check.mjs +549 -0
  77. package/skills/progress-mon/scripts/pm-config.mjs +343 -0
  78. package/skills/progress-mon/scripts/pm-data.mjs +394 -0
  79. package/skills/progress-mon/scripts/pm-html.mjs +77 -0
  80. package/skills/progress-mon/scripts/pm-memory.mjs +333 -0
  81. package/skills/progress-mon/scripts/pm-render.mjs +1644 -0
  82. package/skills/progress-mon/scripts/pm-server.mjs +781 -0
  83. package/skills/progress-mon/scripts/pm-sources.mjs +501 -0
  84. package/skills/progress-mon/scripts/progress-mon.mjs +167 -0
  85. package/skills/ralph-qa/SKILL.md +107 -17
  86. package/skills/ralph-qa/references/gate-evidence.mjs +457 -0
  87. package/skills/ralph-qa/references/payload-scan.mjs +19 -2
  88. package/skills/ralph-qa/references/run-folder.mjs +299 -0
  89. package/skills/ralph-qa/references/seat-payload.mjs +60 -13
  90. package/skills/ralph-qa/references/seat-run.mjs +200 -159
  91. package/skills/remains/SKILL.md +2 -0
  92. package/skills/remains/scripts/boxes.mjs +6 -2
  93. package/skills/setup-bypass-permissions/scripts/codex-config.mjs +19 -8
  94. package/skills/setup-mcp/SKILL.md +1 -1
  95. package/skills/setup-omc-hud/SKILL.md +2 -1
  96. package/skills/setup-omc-hud/assets/codex-config.mjs +19 -8
  97. package/skills/ultra-chain/SKILL.md +32 -14
  98. package/skills/ultra-chain/references/chain-ledger.mjs +111 -3
  99. package/skills/ultra-init/SKILL.md +30 -22
  100. package/skills/ultraqa/NOTICE.md +34 -0
  101. package/skills/ultraqa/SKILL.md +197 -0
  102. package/skills/ultraqa/references/gate-evidence.mjs +457 -0
  103. package/skills/ultraqa/references/run-folder.mjs +299 -0
  104. package/skills/ultraqa/references/tmux-drive.md +52 -0
  105. package/skills/ultraqa/references/uqa-run.mjs +339 -0
  106. package/skills/update-banker/SKILL.md +1 -1
@@ -28,7 +28,9 @@
28
28
  *
29
29
  * Pool state lives in ~/.config/banker/3d-intro/video-pool-state.json (0600). It holds
30
30
  * FINGERPRINTS (sha256 of endpoint+key+model), never a key, so a rotated key or a changed model
31
- * starts with a clean record. State is advisory: a failed write is reported, never fatal.
31
+ * starts with a clean record. A Sora deployment that answered 410 (retired) is recorded under the
32
+ * fingerprint of its endpoint origin and deployment name, and Sora is skipped for 24 h.
33
+ * State is advisory: a failed write is reported, never fatal.
32
34
  * Concurrent processes share the file last-write-wins; the per-key in-flight limit is per process.
33
35
  *
34
36
  * Runtime: Node >=18 builtins only. globalThis.fetch and cp.spawnSync are read at call time so
@@ -40,7 +42,7 @@ import os from 'node:os';
40
42
  import crypto from 'node:crypto';
41
43
  import cp from 'node:child_process';
42
44
  import {
43
- createVideo, pollVideo, downloadVideo, resolveFfmpeg, redact, fetchGuarded, safeForKey, keyHeldBack,
45
+ createVideo, pollVideo, downloadVideo, resolveFfmpeg, redact, fetchGuarded, safeForKey, keyHeldBack, probeSoraDeployment,
44
46
  API_TIMEOUT_MS, DOWNLOAD_TIMEOUT_MS, HELD_KEY, errnoOf, timeLeft,
45
47
  } from './azure-adapter.mjs';
46
48
 
@@ -213,13 +215,17 @@ function updateEntry(file, entry, fn, emit) {
213
215
  }
214
216
  }
215
217
 
216
- /** Clear recorded state for all entries, or only for the given labels (e.g. ['WAN_2']). */
218
+ /**
219
+ * Clear recorded state for all entries, or only for the given labels (e.g. ['WAN_2']). 'AZURE_SORA'
220
+ * clears the Sora retirement record (every Sora deployment's, so creds are not needed for it).
221
+ */
217
222
  export function resetPoolState({ statePath = defaultStatePath(), creds, labels } = {}) {
218
223
  const state = loadPoolState(statePath);
219
224
  if (!labels) state.entries = {};
220
225
  else {
221
226
  const fps = new Set(loadWanPool(creds).filter((e) => labels.includes(e.label)).map((e) => e.fp));
222
- for (const fp of Object.keys(state.entries)) if (fps.has(fp)) delete state.entries[fp];
227
+ const sora = labels.includes(SORA_LABEL);
228
+ for (const [fp, st] of Object.entries(state.entries)) if (fps.has(fp) || (sora && st?.label === SORA_LABEL)) delete state.entries[fp];
223
229
  }
224
230
  return savePoolState(state, statePath);
225
231
  }
@@ -235,14 +241,62 @@ function entryStatus(st, t) {
235
241
  return { status: 'ok' };
236
242
  }
237
243
 
238
- /** Per-entry availability for reporting. Never includes a key, nor a malformed endpoint or model. */
244
+ /**
245
+ * Per-entry availability for reporting: one row per WAN entry (provider 'wan'), then one Sora row
246
+ * (label AZURE_SORA, provider 'sora', status ok | retired | unset | malformed). Never includes a key,
247
+ * nor a malformed endpoint or model.
248
+ */
239
249
  export function poolSummary({ creds = {}, statePath = defaultStatePath(), now = Date.now() } = {}) {
240
250
  const state = loadPoolState(statePath);
241
- return loadWanPool(creds).map((e) => {
251
+ const wan = loadWanPool(creds).map((e) => {
242
252
  const st = state.entries[e.fp];
243
253
  const field = malformedField(e);
244
- return { ...shownEntry(e), okCount: st?.okCount || 0, ...(field ? { status: 'malformed', reason: field } : entryStatus(st, now)) };
254
+ return { ...shownEntry(e), provider: 'wan', okCount: st?.okCount || 0, ...(field ? { status: 'malformed', reason: field } : entryStatus(st, now)) };
245
255
  });
256
+ return [...wan, soraSummary(creds, state, now)];
257
+ }
258
+
259
+ // The Sora row of poolSummary.
260
+ function soraSummary(creds, state, t) {
261
+ const conn = soraConn(creds);
262
+ const base = { label: SORA_LABEL, provider: 'sora', model: isModelId(conn.model) ? conn.model : '(invalid model)' };
263
+ if (!conn.endpoint || !conn.key) return { ...base, host: null, status: 'unset' };
264
+ if (!originOf(conn.endpoint)) return { ...base, host: '(invalid endpoint)', status: 'malformed', reason: 'endpoint' };
265
+ const rec = retiredIn(state, conn, t);
266
+ const status = rec ? { status: 'retired', reason: rec.reason, retiredAt: rec.retiredAt, until: rec.retiredAt + SORA_RETIRED_TTL_MS } : { status: 'ok' };
267
+ return { ...base, host: hostOf(originOf(conn.endpoint)), ...status };
268
+ }
269
+
270
+ /**
271
+ * The CLIP_MODE a run starts with (FREE, nothing is sent): dive without exits on any provider (the user
272
+ * found it the most natural of the three on the sora-2 comparison page). Returns { mode, exit, provider,
273
+ * measured, reason }: provider is the first one generateClip would really use (VIDEO_PROVIDER_ORDER), and
274
+ * measured is false on WAN (dive on WAN is not measured; say so when the user picks). mode is null when
275
+ * no leg could be made: every WAN entry refused (generateClip stops there and does not use Sora), a Sora
276
+ * endpoint that is not https, or nothing set up (no WAN entry, Sora unset or recorded retired).
277
+ */
278
+ export function defaultClipMode(creds = {}, { statePath = defaultStatePath(), now = Date.now() } = {}) {
279
+ for (const p of parseProviderOrder(creds.VIDEO_PROVIDER_ORDER)) {
280
+ const r = p === 'wan' ? wanMode(creds) : soraMode(creds, loadPoolState(statePath), now);
281
+ if (r) return r;
282
+ }
283
+ return { mode: null, provider: null, reason: 'no video provider: no WAN entry, and Sora is unset or recorded retired' };
284
+ }
285
+
286
+ function wanMode(creds) {
287
+ const pool = loadWanPool(creds);
288
+ if (!pool.length) return null;
289
+ if (pool.every((e) => entryRefusal(e))) return { mode: null, provider: 'wan', reason: 'every WAN entry is malformed or held back: generateClip stops there and does not use Sora' };
290
+ return { mode: 'dive', exit: false, provider: 'wan', measured: false, reason: 'the video runs on WAN: dive without exits (dive on WAN is not measured)' };
291
+ }
292
+
293
+ function soraMode(creds, state, t) {
294
+ const conn = soraConn(creds);
295
+ if (!conn.endpoint || !conn.key) return null;
296
+ const origin = originOf(conn.endpoint);
297
+ if (!origin || proxyHolds(origin)) return { mode: null, provider: 'sora', reason: 'AZURE_SORA_ENDPOINT is not an https URL (or the env proxy holds the key back): the leg stops before sending' };
298
+ if (retiredIn(state, conn, t)) return null;
299
+ return { mode: 'dive', exit: false, provider: 'sora', measured: true, reason: 'the video runs on Sora: dive without exits (measured on sora-2 2025-12-08)' };
246
300
  }
247
301
 
248
302
  // In-process in-flight counter per fingerprint (the documented limit is per key).
@@ -728,6 +782,60 @@ function soraConn(creds) {
728
782
  };
729
783
  }
730
784
 
785
+ // ---------------------------------------------------------------------------
786
+ // Sora retirement (HTTP 410). A 410 on the submit is the model's retirement at once. A 410 on a poll
787
+ // or a download may also be a job that expired, so it counts only when the free deployment probe says
788
+ // retired. The record keeps Sora from being sent the next legs for 24 h.
789
+ // ---------------------------------------------------------------------------
790
+
791
+ const SORA_LABEL = 'AZURE_SORA';
792
+ const SORA_RETIRED_TTL_MS = 24 * 3_600_000;
793
+ // The Sora record's name in the state file: endpoint origin and deployment, never the key.
794
+ const soraFp = (conn) => `sora:${crypto.createHash('sha256').update(`${originOf(conn.endpoint)}\n${conn.model}`).digest('hex').slice(0, 16)}`;
795
+
796
+ function retiredIn(state, conn, t) {
797
+ const st = state.entries[soraFp(conn)];
798
+ return st?.status === 'retired' && t - st.retiredAt < SORA_RETIRED_TTL_MS ? st : null;
799
+ }
800
+
801
+ function recordSoraRetired(ctx, conn, stage) {
802
+ const t = ctx.now();
803
+ updateEntry(ctx.statePath, { fp: soraFp(conn), label: SORA_LABEL }, (st) => ({ ...st, status: 'retired', retiredAt: t, reason: `HTTP 410 (${stage})` }), ctx.emit);
804
+ ctx.emit({ type: 'fail', provider: 'sora', label: SORA_LABEL, kind: 'retired', status: 410, stage });
805
+ }
806
+
807
+ const soraWhere = (conn) => `Sora deployment ${isModelId(conn.model) ? conn.model : '(invalid model)'} on ${hostOf(originOf(conn.endpoint))}`;
808
+ const SORA_RETIRED_NEXT = "add a WAN key (3d-intro-setup), or deploy a newer Sora model and clear the record with resetPoolState({ labels: ['AZURE_SORA'] })";
809
+
810
+ function soraRetiredStop(conn, stage, taskId) {
811
+ const job = taskId ? `job ${taskId} may have billed` : 'no job was made';
812
+ return stopError('retired', `${soraWhere(conn)} is retired (HTTP 410 on the ${stage}); ${job}; Sora is skipped for 24 h — ${SORA_RETIRED_NEXT}`, { provider: 'sora', label: SORA_LABEL, status: 410, ...(taskId ? { taskId } : {}) });
813
+ }
814
+
815
+ // Sora was skipped for its record, and no other provider took the leg.
816
+ function soraSkippedStop(conn, rec) {
817
+ return stopError('retired', `${soraWhere(conn)} was recorded retired (${rec.reason}) at ${new Date(rec.retiredAt).toISOString()}, and no WAN key could take the leg; nothing was sent — ${SORA_RETIRED_NEXT}`, { provider: 'sora', label: SORA_LABEL, retiredAt: rec.retiredAt });
818
+ }
819
+
820
+ // One free probe per process and deployment.
821
+ const soraProbes = new Map();
822
+ function probeSoraOnce(conn, now) {
823
+ const fp = soraFp(conn);
824
+ if (!soraProbes.has(fp)) {
825
+ soraProbes.set(fp, probeSoraDeployment({ endpoint: conn.endpoint, key: conn.key, deployment: conn.model, now }).catch(() => ({ state: 'unknown', status: null })));
826
+ }
827
+ return soraProbes.get(fp);
828
+ }
829
+
830
+ // The retired stop for a 410 on a poll or a download when the probe confirms it, else null.
831
+ async function confirmRetired(ctx, conn, stage, jobId) {
832
+ const p = await probeSoraOnce(conn, ctx.now);
833
+ ctx.emit({ type: 'probe', provider: 'sora', label: SORA_LABEL, stage, state: p.state, status: p.status ?? null });
834
+ if (p.state !== 'retired') return null;
835
+ recordSoraRetired(ctx, conn, stage);
836
+ return soraRetiredStop(conn, stage, jobId);
837
+ }
838
+
731
839
  // Poll an accepted Sora job to a downloaded clip; same stop rules as finishWanTask.
732
840
  async function finishSoraJob(ctx, jobId, secs) {
733
841
  const conn = soraConn(ctx.creds);
@@ -737,13 +845,14 @@ async function finishSoraJob(ctx, jobId, secs) {
737
845
  if (!st || !SORA_DONE.has(st.status)) {
738
846
  throw stopError('timeout', `Sora job ${jobId} still ${st?.status ?? 'unknown'} after polling for ${ctx.now() - t0} ms; resume with resumeClip`, { provider: 'sora', taskId: jobId, label: 'AZURE_SORA', seconds: secs });
739
847
  }
740
- const mp4 = await downloadSoraClip(conn, jobId, secs);
848
+ const mp4 = await downloadSoraClip(ctx, conn, jobId, secs);
741
849
  ctx.emit({ type: 'done', provider: 'sora', label: 'AZURE_SORA', taskId: jobId });
742
850
  return { mp4, provider: 'sora', label: 'AZURE_SORA', model: conn.model, seconds: secs, taskId: jobId, usage: null };
743
851
  }
744
852
 
745
853
  // pollVideo throws on a 4xx other than 408/429 (the job is gone, or the key is refused): such a job
746
- // never turns terminal, so the leg ends at once as task_lost instead of polling to the deadline.
854
+ // never turns terminal, so the leg ends at once as task_lost instead of polling to the deadline. A 410
855
+ // the deployment probe calls retired ends it as retired.
747
856
  async function pollSoraJob(ctx, conn, jobId) {
748
857
  const interval = Math.max(0, ctx.pollIntervalMs);
749
858
  try {
@@ -754,6 +863,8 @@ async function pollSoraJob(ctx, conn, jobId) {
754
863
  });
755
864
  } catch (e) {
756
865
  if (!e.status) throw e;
866
+ const retired = e.status === 410 && await confirmRetired(ctx, conn, 'poll', jobId);
867
+ if (retired) throw retired;
757
868
  throw stopError('task_lost', `Sora poll for job ${jobId} failed: HTTP ${e.status}${e.code ? ` ${e.code}` : ''}; check the job in Azure before resubmitting`, { provider: 'sora', taskId: jobId, label: 'AZURE_SORA', status: e.status, code: e.code });
758
869
  }
759
870
  }
@@ -764,10 +875,12 @@ function soraFailure(st, jobId) {
764
875
  return stopError('failed', `Sora job ${jobId} ended ${st.status}: ${JSON.stringify(st.error || '')}`, { provider: 'sora', taskId: jobId, code });
765
876
  }
766
877
 
767
- async function downloadSoraClip(conn, jobId, secs) {
878
+ async function downloadSoraClip(ctx, conn, jobId, secs) {
768
879
  try {
769
880
  return await downloadVideo({ endpoint: conn.endpoint, key: conn.key, id: jobId, apiVersion: conn.apiVersion });
770
881
  } catch (err) {
882
+ const retired = err.status === 410 && await confirmRetired(ctx, conn, 'download', jobId);
883
+ if (retired) throw retired;
771
884
  throw stopError('download', `${err.message} for Sora job ${jobId}; resume with resumeClip`, { provider: 'sora', taskId: jobId, label: 'AZURE_SORA', seconds: secs });
772
885
  }
773
886
  }
@@ -784,7 +897,8 @@ const SORA_SUBMIT_TRIES = 5;
784
897
 
785
898
  // A 429 is a refusal, so retrying it cannot double-bill; a lost connection or a bare 5xx can. Each
786
899
  // wait is the server's Retry-After, else 30 s doubling, from 1 s to 10 min. A Retry-After longer
787
- // than maxRateWaitMs is not waited out: the leg stops as `rate` at once, as WAN's waits do.
900
+ // than maxRateWaitMs is not waited out: the leg stops as `rate` at once, as WAN's waits do. A 410 is
901
+ // the deployment's retirement: recorded, and the leg stops as `retired`.
788
902
  async function submitSora(ctx, conn, secs) {
789
903
  for (let i = 0; ; i++) {
790
904
  try {
@@ -793,6 +907,7 @@ async function submitSora(ctx, conn, secs) {
793
907
  endpoint: conn.endpoint, key: conn.key, model: conn.model, prompt: ctx.prompt, size: ctx.size, seconds: secs, inputReferencePng: ctx.firstFramePng, apiVersion: conn.apiVersion,
794
908
  });
795
909
  } catch (e) {
910
+ if (e.status === 410) { recordSoraRetired(ctx, conn, 'submit'); throw soraRetiredStop(conn, 'submit'); }
796
911
  if (e.status !== 429 || i >= SORA_SUBMIT_TRIES - 1) throw soraSubmitError(e);
797
912
  const want = e.retryAfterMs ?? RATE_BASE_MS * 2 ** i;
798
913
  if (want > ctx.maxRateWaitMs) throw soraRateStop(want, ctx.maxRateWaitMs, e.retryAfterMs != null);
@@ -815,6 +930,10 @@ function soraSubmitError(e) {
815
930
  if (e.status === 429) {
816
931
  return stopError('rate', `Sora still answered HTTP 429 after ${SORA_SUBMIT_TRIES} tries; no job was made — wait, then generate the leg again`, { provider: 'sora', status: 429 });
817
932
  }
933
+ if (e.status === 404) {
934
+ // only the status and the code field are read: a message is no contract
935
+ return stopError('invalid_request', `${e.message}; the deployment name may be wrong, or the deployment was removed after its model retired — check AZURE_SORA_DEPLOYMENT (3d-intro-setup §4 checks it for free)`, { provider: 'sora', status: 404, code: e.code || null });
936
+ }
818
937
  if (soraMayHaveJob(e)) {
819
938
  const answer = e.status === 0 ? e.errno || 'network' : `HTTP ${e.status}`;
820
939
  return stopError('submit_unknown', `Sora submit got no usable answer (${answer}); the job may exist — check Azure before resubmitting`, { provider: 'sora', status: e.status });
@@ -851,6 +970,10 @@ const DEFAULTS = { size: '720x1280', seconds: 5, pollIntervalMs: 15_000, pollTim
851
970
  * kind 'unreachable' Sora host refused the connection (nothing was sent)
852
971
  * kind 'rate' Sora kept answering 429, or asked (Retry-After, err.retryAfterMs) for a
853
972
  * wait past maxRateWaitMs (no job was made); wait, then call again
973
+ * kind 'retired' the Sora deployment answered 410 (on the submit, or on a poll or a
974
+ * download the free deployment probe confirmed), or was recorded so in
975
+ * the last 24 h and no WAN key could take the leg; add a WAN key, or
976
+ * deploy a newer model and resetPoolState({ labels: ['AZURE_SORA'] })
854
977
  * @returns {Promise<{mp4:Buffer, provider:'wan'|'sora', label:string, model:string, seconds:number, taskId:string, usage:object|null}>}
855
978
  */
856
979
  export async function generateClip(opts = {}) {
@@ -867,17 +990,24 @@ export async function generateClip(opts = {}) {
867
990
  const clip = await tryProvider(ctx, p, tried);
868
991
  if (clip) return clip;
869
992
  }
993
+ if (ctx.soraRetired) throw soraSkippedStop(soraConn(o.creds), ctx.soraRetired);
870
994
  throw new Error(`generateClip: no video provider available (tried: ${tried.join(', ') || 'none'})`);
871
995
  }
872
996
 
873
997
  // One provider's go at the leg: the clip, or null to go on to the next. Sora, once it runs, ends the
874
- // leg (with a clip or a throw).
998
+ // leg (with a clip or a throw). A Sora deployment recorded retired is skipped before anything is sent.
875
999
  async function tryProvider(ctx, p, tried) {
876
1000
  if (p === 'wan') {
877
1001
  tried.push('wan');
878
1002
  return runWan(ctx);
879
1003
  }
880
1004
  if (p !== 'sora' || !soraReady(ctx)) return null;
1005
+ const rec = retiredIn(loadPoolState(ctx.statePath), soraConn(ctx.creds), ctx.now());
1006
+ if (rec) {
1007
+ ctx.emit({ type: 'skip', provider: 'sora', label: SORA_LABEL, reason: 'retired', retiredAt: rec.retiredAt });
1008
+ ctx.soraRetired = rec;
1009
+ return null;
1010
+ }
881
1011
  tried.push('sora');
882
1012
  ctx.emit({ type: 'fallback', provider: 'sora', from: tried.slice(0, -1) });
883
1013
  return runSora(ctx);
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: all-in-one
3
- description: "코딩 작업을 한 명령으로 계획부터 QA 통과까지 엄격하게 진행. `ralplan` 으로 합의 계획, `ralph` 로 검증 구현을 하고 독립 QA 게이트(Claude Code 는 `verify` 순환, Codex 는 `ultraqa`)로 마무리. 'all-in-one'/'all in one' 시 사용. 한 줄 수정은 이 스킬 대신 `ralph`, 아이디어 확장은 `autopilot`."
4
- argument-hint: "[--short] [--checkpoint] [--critic=critic|architect|codex] [--qa=tests|build|lint|typecheck] [--no-deslop] <task description>"
3
+ description: "코딩 작업을 한 명령으로 계획부터 QA 통과까지 엄격하게 진행. `ralplan` 으로 합의 계획, `ralph` 로 검증 구현을 하고, 두 런타임 모두 `ralph-qa` 의 독립 검토자 3명 이상과 실행기가 돌린 게이트 증거로 마무리. 'all-in-one'/'all in one' 시 사용. 한 줄 수정은 이 스킬 대신 `ralph`, 아이디어 확장은 `autopilot`."
4
+ argument-hint: "[--short] [--checkpoint] [--critic=critic|architect|codex] [--qa=tests,build,lint,typecheck,scenarios] [--codex[=<model>]] [--gemini[=<model>]] [--opencode[=<provider/model>]] [--no-deslop] <task description>"
5
5
  level: 4
6
6
  ---
7
7
 
@@ -11,8 +11,8 @@ level: 4
11
11
 
12
12
  ## 0. Prerequisite: OMC 5 with a matching `omc` CLI (check first)
13
13
 
14
- This skill orchestrates OMC's `ralplan` → `ralph`, then a QA gate built on OMC's
15
- `verify` workflow. **Check the dependency before Stage 1**; if it is missing or
14
+ This skill orchestrates OMC's `ralplan` → `ralph`, then banker's own `ralph-qa`
15
+ as the QA gate. **Check the dependency before Stage 1**; if it is missing or
16
16
  stale, **guide installation first** rather than failing mid-pipeline. The `omc`
17
17
  check below is for Claude Code; on Codex check OMX instead (`omx --version`), whose
18
18
  `ralph` does not use the `omc` CLI.
@@ -33,11 +33,15 @@ check below is for Claude Code; on Codex check OMX instead (`omx --version`), wh
33
33
  → oh-my-claudecode, the `setup-omc` skill, or
34
34
  `npm install -g oh-my-claude-sisyphus@latest`), then resume. Do not start a
35
35
  pipeline whose Stage 2 will stop.
36
- - **`ultraqa` does not exist in OMC 5** (removed in 5.0.0, no alias), so a call
37
- to `oh-my-claudecode:ultraqa` fails. Stage 3 below runs in its place.
36
+ - **Stage 3 is banker's `ralph-qa` on both runtimes.** It ships with banker, so
37
+ the gate needs no OMC or OMX skill; OMC and OMX serve Stages 1 and 2 only.
38
38
  - On Codex the equivalent framework is OMX (`omx setup`).
39
39
 
40
- **런타임 매핑 (Claude Code ↔ Codex).** Stage 1·2 의 `ralplan`·`ralph` 는 Claude Code에선 OMC 스킬이고 **Codex에선 oh-my-codex(OMX)가 제공하는 동명 스킬**이다. Stage 3 은 런타임마다 다르다: Claude Code(OMC 5)는 `ultraqa` 가 없어 아래 verify 순환을 직접 돌리고, **Codex는 OMX 가 아직 제공하는 `ultraqa`** 를 쓴다. 아래 `Skill("oh-my-claudecode:<name>")`·`Task(subagent_type="oh-my-claudecode:<name>")` 표기는 Claude Code 기준이다.
40
+ **런타임 매핑 (Claude Code ↔ Codex).** Stage 1·2 의 `ralplan`·`ralph` 는 Claude Code에선 OMC 스킬이고 **Codex에선 oh-my-codex(OMX)가 제공하는 동명 스킬**이다. Stage 3 은 두 런타임 모두 banker `ralph-qa` 다: Claude Code `Skill("banker:ralph-qa")`, Codex npm 길 `$banker-ralph-qa`, 플러그인 길 `$banker:ralph-qa`. Codex 에는 Skill 도구가 없으니 그 `SKILL.md` 를 읽고 그대로 따른다(npm 길 `<S>/../banker-ralph-qa/SKILL.md`, 플러그인 길 `<S>/../ralph-qa/SKILL.md`, `<S>` 는 이 스킬 폴더). `<RQ>` 는 ralph-qa 의 `references` 폴더다(Claude Code 와 Codex 플러그인 길 `<S>/../ralph-qa/references`, Codex npm 길 `<S>/../banker-ralph-qa/references`). 아래 `Skill("oh-my-claudecode:<name>")` 표기는 Claude Code 기준이다.
41
+
42
+ **Codex 의 tmux 밖 실행.** OMX 의 런타임 워크플로(`ralph`, `team` 등)는 OMX tmux 런타임을 요구한다. OMX 의 `AGENTS.md` 는 tmux 밖(Codex App, 일반 `codex`, `codex exec`)이면 그 사실을 알리고 가장 가까운 대체 경로로 계속하라고 정한다(oh-my-codex 0.20.2 `templates/AGENTS.md`). 그래서 tmux 밖이라는 사실만으로는 멈추지 않는다. 그 단계는 이 세션이 그 스킬의 `SKILL.md` 절차를 네이티브 하위 에이전트로 그대로 따르고, 보고에 `<단계>: tmux 밖 대체 실행(OMX 런타임 없음)` 한 줄을 적는다. 종결 낱말은 다른 멈춤 조건과 마지막 ralph-qa 판정으로만 정한다. 대체 실행도 할 수 없으면(하위 에이전트 기능이 꺼짐 등) `STOPPED(omx-runtime-unavailable)` 로 멈추고 tmux 안의 OMX 에서 다시 실행하라고 안내한다.
43
+
44
+ **Codex 의 계획과 상태 경로.** 이 문서의 `.omc/plans/`, `.omc/state/` 는 Claude Code(OMC) 경로다. Codex 에서는 OMX 경로를 쓴다: 계획은 `.omx/plans/`(ralplan 이 쓰는 `prd-*.md`, `test-spec-*.md`), ralph 의 진행 기록은 `.omx/state/<scope>/ralph-progress.json`. Codex 에서 `.omc/` 아래에 계획을 쓰지 않는다.
41
45
 
42
46
  ## Purpose
43
47
 
@@ -51,18 +55,17 @@ Stage 1 /ralplan --deliberate consensus plan (Planner → Architect → Cri
51
55
  Stage 2 /ralph --critic=critic PRD-driven persistent build, critic-verified
52
56
  │ → implementation + deslop + regression, then ralph self-cancels
53
57
  ▼
54
- Stage 3 QA gate (--qa=tests) fresh verifier → diagnose → fix, bounded cycle
55
- → PASS, or honest STOPPED-with-diagnosis (Codex: OMX /ultraqa)
58
+ Stage 3 ralph-qa --gates=<--qa> 3+ independent reviewers on runner-run gate evidence
59
+ → ALL-IN-ONE COMPLETE, or honest STOPPED (both runtimes)
56
60
  ```
57
61
 
58
62
  This skill is a **thin sequential orchestrator**. It does not implement, plan,
59
- or test directly, and it does **not** create its own state file. Stages 1 and 2
60
- hand every loop, retry, and verification to the sub-skill they invoke. The one
61
- loop all-in-one owns is Stage 3's bounded verify → fix cycle: OMC 5 removed
62
- `ultraqa`, which used to own it, and its replacement `verify` gathers evidence
63
- but does not fix. Beyond that, its only job is to run the stages in order,
64
- bridge their handoffs, gate each stage on the previous one's success, and
65
- report honestly.
63
+ or test directly, and it does **not** create its own state file. Each stage
64
+ hands every loop, retry, and verification to the skill it invokes: `ralplan`,
65
+ `ralph`, and banker's `ralph-qa`, whose judge → fix loop runs until its quorum
66
+ says `APPROVE` or its `--max` ends it. Beyond that, its only job is to record the
67
+ pre-change gate baseline, run the stages in order, bridge their handoffs, gate
68
+ each stage on the previous one's success, and report honestly.
66
69
 
67
70
  ## Why three stages (and not just ralph)
68
71
 
@@ -75,17 +78,20 @@ modes, and each stage's reviewer is independent of the previous stage's author
75
78
  2. **A partial or unverified build declared "done"** → Stage 2's PRD
76
79
  story-by-story persistence + critic verification catch this.
77
80
  3. **Integration / regression breakage slipping past the builder's own checks**
78
- → Stage 3's fresh, test-focused, fix-cycling gate catches this.
81
+ → Stage 3's independent quorum over gate evidence the runner, not a model,
82
+ recorded catches this.
79
83
 
80
84
  **On the apparent redundancy of Stage 3:** ralph (Stage 2) already runs its own
81
85
  Step-7 reviewer verification and Step-7.6 regression re-run (`omc ralph verify`
82
86
  against the baseline it recorded). Stage 3 is still worth running because it is
83
- *independent* (a fresh verifier agent, not the context that built the change),
84
- *test-suite-focused*, and *actively fixes* in a bounded cycle. If ralph's
85
- regression already ran the suite green, the gate usually passes in cycle 1
86
- (cheap); its value rises when the suite is broad, when changes touched many
87
- files, or when integration/flaky issues hide behind unit-level checks. Keeping
88
- it explicit gives a clean, separate PASS signal.
87
+ *independent* (3+ reviewers in contexts that did not build the change),
88
+ *evidence-bound* (each `--qa` goal's real command is run by ralph-qa's
89
+ `gate-evidence.mjs` and kept as an `observed` record; a model's word about a
90
+ command does not count), and *actively fixes* inside ralph-qa's bounded loop.
91
+ If ralph's regression already ran the suite green, the gate usually approves in
92
+ its first iteration (cheap); its value rises when the suite is broad, when
93
+ changes touched many files, or when integration/flaky issues hide behind
94
+ unit-level checks. Keeping it explicit gives a clean, separate PASS signal.
89
95
 
90
96
  ## When to use
91
97
 
@@ -115,7 +121,8 @@ it explicit gives a clean, separate PASS signal.
115
121
  | `--short` | Stage 1 runs ralplan **without** `--deliberate` (skips pre-mortem + expanded test plan) for lower-risk work | off → deliberate |
116
122
  | `--checkpoint` | Insert an `AskUserQuestion` approval gate between Stage 1 (plan) and Stage 2 (implement) | off → auto-proceed |
117
123
  | `--critic=critic\|architect\|codex` | Reviewer passed to ralph's verification | `critic` |
118
- | `--qa=tests\|build\|lint\|typecheck` | What the Stage 3 gate checks (Codex: the goal passed to ultraqa) | `tests` |
124
+ | `--qa=<goals>` | The Stage 3 gates, comma-separated: `tests`, `build`, `lint`, `typecheck`, `scenarios` (adversarial scenario rows). Passed to ralph-qa as `--gates` on both runtimes | `tests` |
125
+ | `--codex[=<model>]`, `--gemini[=<model>]`, `--opencode[=<provider/model>]` | Passed through to ralph-qa as external seats. Without them only its backbone of 3+ session-model reviewers sits | none |
119
126
  | `--no-deslop` | Passed through to ralph (skip the post-review deslop pass) | off |
120
127
 
121
128
  Everything before the flags / after them that isn't a recognized flag is the
@@ -147,18 +154,43 @@ then **stops**.
147
154
 
148
155
  ### Before Stage 2 — record what already fails
149
156
 
150
- Run the command(s) of the `--qa` goal once on the unchanged tree and keep the
151
- failing items (test names, error lines) in this conversation. This
152
- **pre-change record** is the gate's baseline: it covers checks that are not
153
- among ralph's feedback commands, and on Codex it is the only baseline there is
154
- (OMX `ralph` keeps none). Never use `git stash` or `git reset` to get a
155
- pre-change state for it; if the tree already has changes, take the record from
156
- a clean copy of HEAD (`git worktree add --detach <tmp> HEAD`, run there, then
157
+ Make the gate folder and run each `--qa` goal's command once on the unchanged
158
+ tree, through ralph-qa's runner, as iteration 0:
159
+
160
+ ```bash
161
+ node '<RQ>/gate-evidence.mjs' init --cwd '<repo>' # its "dir" is the gate folder <g>
162
+ node '<RQ>/gate-evidence.mjs' run --dir '<g>' --id tests --cmd '<the tests command>' --timeout 600 --iter 0 --cwd '<repo>'
163
+ ```
164
+
165
+ One command per gate (no `&&` or `;` chains) and a `--timeout` on each.
166
+ `scenarios` has no baseline: its rows test the change. This **pre-change
167
+ record** (iteration 0 in `<g>/evidence.md`) is the gate's baseline: ralph-qa
168
+ counts a failure with the same command and failure signature as not this
169
+ change's (`baseline`). It covers checks that are not among ralph's feedback
170
+ commands, and on Codex it is the only baseline there is (OMX `ralph` keeps
171
+ none). Never use `git stash` or `git reset` to get a pre-change state for it; if
172
+ the tree already has changes, take the record from a clean copy of HEAD
173
+ (`git worktree add --detach <tmp> HEAD`, run with `--cwd <tmp>`, then
157
174
  `git worktree remove <tmp>`). A fresh worktree has no installed dependencies
158
175
  (`node_modules`, a venv): link or install them there first, and confirm the
159
176
  commands really ran — a record made only of "command not found" errors is no
160
- baseline. The record lives only in this conversation; if the session may
161
- compact or end before Stage 3, put it in the `ready-compact` note.
177
+ baseline. The runner writes the folder a command ran in as `<repo>` in the
178
+ failure signature, so a failure in the copy and the same failure in the repo
179
+ match. On Codex the runner writes outside the workspace: ask for approval to
180
+ run it outside the sandbox. The record lives in `<g>`; if the session may
181
+ compact or end before Stage 3, put the `<g>` path in the `ready-compact` note.
182
+ The gate folder lives until Stage 3 has its end word: no cleanup's day rule
183
+ takes it (ralph-qa's gate folder rule).
184
+
185
+ **Not a git repo.** If `init` (or `run`) stops with `not-a-git-tree` (exit 3,
186
+ nothing made or run), first check that the folder the stop names (`checked` in
187
+ its JSON, an absolute path) is the target repo; if not, call it again with
188
+ `--cwd '<repo>'`. Only when the target really is no git repo: the gates cannot
189
+ have evidence (the work tree fingerprint sees no change). Take no baseline, and in Stage 3 hand ralph-qa no `--gates`; it
190
+ then writes `게이트: 선언 없음(git 저장소 아님)` in its report and seat
191
+ instructions. Never run `git init` yourself: tell the user that `git init` and a
192
+ first commit make the gates work next time. The same on both runtimes (Claude
193
+ Code, and Codex on the npm and the plugin route).
162
194
 
163
195
  ### Stage 2 — Implement: `/ralph`
164
196
 
@@ -208,58 +240,59 @@ criteria → Step-7 reviewer verification → Step-7.5 deslop → Step-7.6 regre
208
240
  hand; never stash or reset.
209
241
  - **Carry the baseline forward.** ralph's own baseline (first iteration) also
210
242
  lists failures that existed before the change; ralph treats them as noise
211
- and reports them at Step 8. Note its session id (the `<id>` in its PRD path
212
- `.omc/state/sessions/<id>/`) for Stage 3.
243
+ and reports them at Step 8. Keep that list for the final report, beside the
244
+ gate baseline in `<g>`.
213
245
  - **If ralph reports a fundamental blocker** (same issue across 3+ iterations,
214
246
  missing credentials, unclear requirement, external dependency down, or the
215
247
  `omc ralph verify` command unavailable): stop and report. Do **not** run
216
248
  Stage 3 on a broken or partial build and call it done.
217
249
 
218
- ### Stage 3 — QA gate
250
+ ### Stage 3 — QA gate: banker `ralph-qa` (both runtimes)
219
251
 
220
252
  Precondition: Stage 2's ralph has completed and self-cancelled.
221
253
 
222
- **Claude Code (OMC 5):** run the bounded cycle that `ultraqa` used to own —
223
- the same shape as OMC 5's autopilot QA phase. No state file: the cycle count
224
- lives in this conversation.
225
-
226
- 1. **Verify, independently.** `Task(subagent_type="oh-my-claudecode:verifier", ...)`
227
- — a fresh context that did not build the change. Brief it to follow the
228
- `oh-my-claudecode:verify` workflow for the `--qa` goal (`tests` by default;
229
- `build`, `lint` or `typecheck` select that check), run the project's real
230
- commands, and return **PASS** or **FAIL** with the commands, their output and
231
- each failing item.
232
- Whether a failure predates the change is not judged by eye. A failure is
233
- **pre-existing only** when the pre-change record lists it, or — Claude
234
- Code — when `omc ralph verify --session <ralph's session id> --json` leaves
235
- it out of `newFailures` (cancel leaves the baseline in place; check that the
236
- JSON reports `baselinePresent: true` — without a baseline it reports every
237
- current failure as new). **Everything else is new**, and so is a
238
- pre-existing failure that points at a file the change touched (as in ralph).
239
- If ralph was run again in this session (a resumed run), its first iteration
240
- rewrote that baseline on the half-changed tree; then judge by the
241
- pre-change record alone.
242
- 2. **PASS** → the gate is met; go to the final report.
243
- 3. **FAIL** → `Task(subagent_type="oh-my-claudecode:architect", ...)` diagnoses
244
- the root cause (read-only), then `Task(subagent_type="oh-my-claudecode:executor", ...)`
245
- makes the smallest fix inside the change's scope. The next cycle verifies
246
- again with a **fresh** verifier, never the one that saw the failure.
247
-
248
- Limits: at most 5 verifications. When the 5th still fails, stop without
249
- another fix (a fix nobody verified is not a result). Stop early when the same
250
- failure (same test, same error signature) comes back 3 times — that is a
251
- fundamental problem for the user, not for another cycle. Never fix a baseline
252
- failure inside this gate; report it.
253
-
254
- **Codex (OMX):** run OMX `$ultraqa` with the goal (OMX still ships it). OMX
255
- keeps no feedback baseline, so hand it the pre-change record, tell it not to
256
- fix those failures, and report them apart; treat its outcome the same way.
257
-
258
- Report the real outcome:
259
- - **Goal met** → "ALL-IN-ONE COMPLETE" with the cycle count.
260
- - **5 failed verifications / same failure 3×** → "STOPPED" with the verifier's
261
- evidence and the architect's diagnosis. Do **not** claim success if the gate did not reach
262
- goal-met.
254
+ Invoke ralph-qa (Claude Code `Skill("banker:ralph-qa")`; Codex
255
+ `$banker-ralph-qa`, plugin `$banker:ralph-qa`) with
256
+ `--gates=<the --qa goals> --lens=both`, plus any `--codex`, `--gemini` or
257
+ `--opencode` all-in-one was given. Hand it:
258
+
259
+ - **What to judge:** the change since `HEAD` as Stage 2 began (commits ralph
260
+ made count as part of it), the plan path from Stage 1, and the plan's
261
+ acceptance criteria.
262
+ - **Gate evidence:** the gate folder `<g>` with its iteration-0 baseline (ralph-qa
263
+ uses it, runs no iteration 0 of its own and does not remove it), and
264
+ for each goal the one command and timeout the baseline ran. Add the
265
+ acceptance criterion "each `--qa` goal's real command ran through the runner
266
+ (`gate-evidence.mjs run`) on the tree being judged, as an `observed` record";
267
+ a model's account of a command (`reported`) is not evidence. `<g>` holds the
268
+ gate records only: ralph-qa sends any external-seat payload from a folder of
269
+ its own.
270
+ - **With `scenarios`:** ralph-qa's nine adversarial classes (malformed input,
271
+ repeated interruption, prompt injection, cancel and resume, stale state, dirty
272
+ work tree, hung command, flaky test, misleading success output) each get a
273
+ scenario row run as `scenario-<n>`, or a one-line reason it does not apply.
274
+ - **Baseline rule:** never fix a baseline failure; report it apart. A baseline
275
+ failure whose failing lines point at a file the change touched counts as new
276
+ (as in ralph): the seats raise it as a blocker.
277
+ - **If `<g>` is gone** (the runner stops with `not-a-run-folder`: a logout or
278
+ reboot emptied `$XDG_RUNTIME_DIR`, or it was removed): make a new one with
279
+ `gate-evidence.mjs init` and hand that, with no iteration 0. Never run
280
+ iteration 0 on the changed tree. Failures that were there before then count
281
+ as this change's: put the line `gate baseline: none (<g> was gone)` in the
282
+ report, with Stage 2's ralph baseline list beside it.
283
+
284
+ ralph-qa owns the loop: its seats judge, the author fixes, it runs the gates
285
+ again and the same seats judge again, up to its `--max` (5). all-in-one adds no
286
+ cycle of its own. When it has its end word, run
287
+ `node '<RQ>/gate-evidence.mjs' cleanup --dir '<g>'` (a `<g>` named `ralph-qa.*`,
288
+ made before this version, goes with `node '<RQ>/seat-run.mjs' cleanup --dir '<g>'`).
289
+
290
+ Report the real outcome by ralph-qa's end word (the word before any parenthesis):
291
+ - **`APPROVE`** → "ALL-IN-ONE COMPLETE" with ralph-qa's verdict line, its
292
+ iteration count and its gate block.
293
+ - **`ITERATE`** (its `--max` spent; `원인 gate` when only the gates held it) or
294
+ **`INCONCLUSIVE`** → "STOPPED" with the verdict line, the gate block and the
295
+ blockers left. Do **not** claim success if the gate did not approve.
263
296
 
264
297
  ## Orchestrator rules
265
298
 
@@ -267,8 +300,8 @@ Report the real outcome:
267
300
  next begins. No skipping, no reordering, no running two stages' loops at once.
268
301
  - Emit one clear progress line at each stage boundary so the user always knows
269
302
  where the pipeline is (`[all-in-one] Stage 1/3 — planning…`, etc.).
270
- - Do not add an all-in-one state file or a second persistence loop. Stages 1–2
271
- own their loops and state; Stage 3's bounded cycle is the only loop here.
303
+ - Do not add an all-in-one state file or a second persistence loop. Stages 1–3
304
+ own their loops and state; all-in-one adds none.
272
305
  - Treat a sub-skill's internal "stop"/"cancel" as the end of *that stage*, never
273
306
  as the end of the pipeline (see the Stage 1 and Stage 2 notes above).
274
307
 
@@ -280,8 +313,9 @@ When the pipeline ends (complete or stopped), give a concise honest summary:
280
313
  (1 line).
281
314
  - **Implementation**: stories/stages completed, files changed, the ralph critic
282
315
  verdict.
283
- - **QA**: the gate's goal, PASS after N cycles or STOPPED-with-diagnosis, and
284
- the baseline failures that predate the change, listed apart.
316
+ - **QA**: the `--qa` goals, ralph-qa's verdict line and gate block (APPROVE, or
317
+ STOPPED with its blockers), and the baseline failures that predate the
318
+ change (the `baseline` gates and ralph's Step-8 list), listed apart.
285
319
  - **Unmet / scope-limited / deferred**: state plainly — never paper over a gate
286
320
  that did not pass.
287
321
 
@@ -291,7 +325,8 @@ When the pipeline ends (complete or stopped), give a concise honest summary:
291
325
  requests don't double-charge; keys stored in payment_requests, 24h TTL"`
292
326
  Specific subsystem, concrete behavior, and a storage hint. Stage 1 plans the
293
327
  schema + flow with a pre-mortem, Stage 2 implements story-by-story with critic
294
- sign-off, Stage 3 runs the test suite as an independent gate. Good fit.
328
+ sign-off, Stage 3's ralph-qa judges the change on the test suite the runner
329
+ ran. Good fit.
295
330
 
296
331
  **Good** — `"/all-in-one --short --qa=typecheck refactor user_context to drop the
297
332
  unused Session import and split the god-object into mixins"`
@@ -311,10 +346,11 @@ first to expand it into a spec.
311
346
  - The `omc` CLI check in §0 fails → guide the install/update; do not start.
312
347
  - Stage 1 cannot reach consensus → surface best plan, ask the user (don't build).
313
348
  - Stage 2 ralph hits a fundamental blocker → stop and report (don't run QA).
314
- - Stage 3 fails its 5th verification / sees the same failure 3× → report the
315
- diagnosis honestly (don't claim success).
349
+ - Stage 3's ralph-qa ends `ITERATE` (its `--max` spent) or `INCONCLUSIVE` →
350
+ report its verdict line and blockers honestly (don't claim success).
316
351
  - User says "stop" / "cancel" / "abort" at any point → run
317
352
  `/oh-my-claudecode:cancel` and stop.
353
+ - After STOPPED, change no file, not even a one-line fix: report only, until the user gives a new instruction. Nothing in this skill (its description's "one-line fix" pointer included) authorizes an edit after a stop.
318
354
 
319
355
  ## Final checklist
320
356
 
@@ -323,8 +359,11 @@ first to expand it into a spec.
323
359
  no-consensus / checkpoint rejection)
324
360
  - [ ] Stage 2 received the plan path, completed all PRD stories, passed critic
325
361
  verification, and self-cancelled before Stage 3
362
+ - [ ] Before Stage 2: each `--qa` goal but `scenarios` ran once through
363
+ `gate-evidence.mjs run --iter 0` on the unchanged tree
326
364
  - [ ] Stages did not overlap (ralph fully ended before the gate began)
327
- - [ ] Stage 3: a fresh verifier reported the chosen goal met (or STOPPED with
328
- diagnosis); baseline failures reported apart, not fixed
365
+ - [ ] Stage 3: ralph-qa (`--gates=<the --qa goals>`) ended `APPROVE` on
366
+ `observed` gate records (or STOPPED with its verdict line); baseline
367
+ failures reported apart, not fixed
329
368
  - [ ] Final report states plan/implementation/QA outcomes and any unmet items
330
369
  plainly
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: omc-patch
3
- description: "OMC 플러그인이 자동 업데이트된 뒤 사라진 훅 패치를 활성 버전에 다시 적용하고 OMC 마켓플레이스 자동 업데이트를 막음. 옛 킷이 남긴 헬퍼 자리 결함(project-memory 꺼짐)도 찾아 고침. 진단, 적용, 검증, 되돌리기. 'omc-patch'/'OMC 패치'/'OMC 업데이트 뒤 패치'/'훅 timed out'/'훅 프로세스 누적'/'PC 느려짐'/'project-memory 안 됨' 시 사용."
3
+ description: "Claude Code 의 OMC 를 다룸(Codex 에서 실행해도 같음). OMC 플러그인이 자동 업데이트된 뒤 사라진 훅 패치를 활성 버전에 다시 적용하고 OMC 마켓플레이스 자동 업데이트를 막음. 옛 킷이 남긴 헬퍼 자리 결함(project-memory 꺼짐)도 찾아 고침. 진단, 적용, 검증, 되돌리기. 'omc-patch'/'OMC 패치'/'OMC 업데이트 뒤 패치'/'훅 timed out'/'훅 프로세스 누적'/'PC 느려짐'/'project-memory 안 됨' 시 사용."
4
4
  argument-hint: "[check | revert]"
5
5
  ---
6
6