@lifeaitools/rdc-skills 0.35.26 → 0.37.0

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.
@@ -111,26 +111,27 @@ fi
111
111
 
112
112
  ⛔ **No fixit closes without a code-review pass.** Even single-file changes go through review.
113
113
 
114
- Dispatch ONE `pr-review-toolkit:code-reviewer` agent on the fixit commit:
115
-
116
- ```
117
- Agent({
118
- subagent_type: "pr-review-toolkit:code-reviewer",
119
- description: "fixit code review",
120
- prompt: "Review `git show HEAD` on the development branch. Focus on:
121
- bugs, logic errors, security, project-convention adherence (.claude/rules/*).
122
- Confidence-based filtering — high-confidence findings only.
123
- Return CODE_REVIEW_COMPLETE with: { critical_count, high_count, medium_count,
124
- low_count, findings: [{severity, file:line, issue, suggested_fix}] }."
125
- })
126
- ```
127
-
128
- **Severity gate:**
129
- - `critical` or `high` findings → fix in this same fixit session (do not escalate to rdc:build for the fix itself; the original fixit owns the cleanup), re-commit, re-run review until clean
130
- - `medium` or `low` findings → record in `implementation_report.flags`; proceed to close
131
- - Zero findings → proceed to close
132
-
133
- Under `RDC_TEST=1`: echo `[RDC_TEST] skipping code-review dispatch` and proceed.
114
+ Run the deterministic review gate in `guides/review-gate.md` with
115
+ `BASE=HEAD~1` (or the commit before the fixit's first commit) and `SCOPE` set
116
+ to the touched package. For a fixit this usually takes seconds.
117
+ Approved: direct operator instruction — "we need to tune our code reviews to be
118
+ specific -- if you ran the validator for solid and lint etc... it should take 1-2
119
+ minutes". 2026-09-21, Dave, in session.
120
+
121
+ Dispatch `pr-review-toolkit:code-reviewer` only for a **named question** the
122
+ validators cannot answer, scoped to the files it touches. Never dispatch it with
123
+ a bare "review the commit".
124
+
125
+ **Gate outcome:**
126
+ - A red gate step, or a critical/high finding from a named-question pass → fix
127
+ it in this same fixit session. Do not escalate the fix to rdc:build; the
128
+ original fixit owns the cleanup. Re-commit and re-run the gate until it is clean.
129
+ - Medium/low findings from a named-question pass → record them in
130
+ `implementation_report.flags` and proceed to close.
131
+ - All steps green → quote the `REVIEW_GATE` receipt in the implementation report
132
+ and proceed to close.
133
+
134
+ Under `RDC_TEST=1`: echo `[RDC_TEST] skipping review gate` and proceed.
134
135
 
135
136
  ### 5.7 Deliver to done (MANDATORY — a fixit is not done until the change is LIVE)
136
137
 
@@ -222,4 +223,10 @@ Report: what was fixed, file(s) changed, commit hash. One sentence.
222
223
 
223
224
  ## Capture lessons (exit step)
224
225
 
225
- Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — write one `.rdc/lessons/<YYYY-MM-DD>-fixit-<short-slug>.md` per lesson using the schema in that spec. Set `scope` (`simple` | `architectural`) and `status` (`open`, or `applied` if you shipped the fix in this same run, with the commit linked). Commit the lesson file(s) on `develop` alongside the run's other commits, and note "N lessons captured" in your verdict/summary. A run that taught nothing writes nothing — absence is the default.
226
+ Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — record each lesson where its fix belongs:
227
+
228
+ - **Fix encoded in this run** (a guard, test or script shipped, commit linked): the lesson goes in that commit's message. No file, no issue.
229
+ - **Fix not encoded**: submit it to the repository the fix belongs to, which opens one `lesson` + `proposal` issue there (an identical title opens nothing):
230
+ `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" submit --repo <owning repo slug> --title "…" --what "…" --root-cause "…" --fix "…"`
231
+
232
+ No lesson file is written. Note "N lessons captured (E encoded, S submitted)" with the issue URLs in your verdict/summary. A run that taught nothing records nothing — absence is the default.
@@ -160,26 +160,36 @@ Write to `.rdc/reports/YYYY-MM-DD-housekeeping.md`:
160
160
 
161
161
  ## Lessons triage (weekly)
162
162
 
163
- Read all `.rdc/lessons/*.md` with `status: open` (schema + procedure: `.rdc/guides/lessons-learned-spec.md` § Triage procedure). Cluster by `area` + root-cause similarity (dedupe repeats into one fix).
163
+ List the open lesson issues across every registered repository (procedure: `.rdc/guides/lessons-learned-spec.md` § Triage procedure):
164
+
165
+ ```bash
166
+ node "$LIFEAI_ENV/bin/rdc-lesson.mjs" list --state open
167
+ ```
168
+
169
+ Every registered repository is listed, skipped because its issues are switched off (reported), or **NOT listed** — no GitHub repository resolves or the read failed. NOT listed is named on stderr and in `--json` `skipped`, and the command exits 1: the triage is then incomplete, not empty. Cluster open `proposal` issues by area + root-cause similarity (dedupe repeats into one fix; close the duplicates, linking the survivor), take reopened recurrences first (their fix did not hold), and decide each cluster's scope: **simple** (one-file fix, config, grep guard, test) or **architectural** (anything in `.claude/rules/architectural-change-approval.md`; when unsure, architectural).
164
170
 
165
171
  ### Attended mode (default — a human is present)
166
172
 
167
173
  For each cluster:
168
174
 
169
- - `scope: simple` → apply the fix directly (rule line, skill-doc edit, config, guard), commit it, set the lesson(s) `status: applied` with the commit linked.
170
- - `scope: architectural` → do NOT edit. Present the issue + options via `AskUserQuestion` (per `.claude/rules/architectural-change-approval.md`). On approval, apply via the correct lifecycle (rdc-skills tag/push for skills; cited commit for rules) and set `status: applied`. If deferred, set `status: triaged` and spawn a `work_item`.
171
- - Not worth fixing → `status: wont-fix` with a one-line reason.
175
+ - **simple** → create the work item, run `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" accept <issue> --repo <slug> --work-item <uuid>`, apply the fix as a guard/test/script, and commit with `Fixes #N`. In regen-root (default branch `main`, integration `develop`) and regen-deploy-mgr (default branch `codex/regen-deploy-mgr-wp1`) `Fixes #N` never reaches the default branch, so once the fix lands run `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" close <issue> --repo <slug> --commit <sha>`.
176
+ - **architectural** → do NOT edit. Present the issue + options via `AskUserQuestion` (per `.claude/rules/architectural-change-approval.md`). On approval, accept it and apply via the correct lifecycle (rdc-skills tag/push for skills; cited commit for rules). If deferred, accept it so the work item carries it.
177
+ - Not worth fixing → close the issue as not planned with a one-line reason.
172
178
 
173
179
  ### Unattended mode (no human — overnight / cron / `rdc:overnight`)
174
180
 
175
181
  When the weekly triage runs unattended, follow `.rdc/guides/lessons-learned-spec.md` § **Triage procedure — UNATTENDED weekly mode** — do not run `AskUserQuestion`. In brief (the spec is authoritative):
176
182
 
177
183
  - **Per-difficulty model routing** (reuses the `rdc:build` table): the run is led by `claude-sonnet-5` for clustering + scope/difficulty triage; mechanical apply → `claude-haiku-4-5`; harder multi-file/migration fix → `claude-sonnet-5`; design/architectural fix → `claude-opus-5`.
178
- - `scope: simple` → apply directly or via `rdc:fixit`, commit, `status: applied`.
179
- - `scope: architectural` with a single clear correct fix (records an already-learned lesson — e.g. "document X", "add a gate", update a guide) → route through `rdc:plan` → `rdc:build` (or `rdc:fixit` if genuinely <5 files), update the documentation, set `status: applied`/`triaged`.
180
- - `scope: architectural` **and genuinely ambiguous** (multiple valid approaches, real tradeoffs) → write a `human_items` row (`item_type='decision'`, with options in `suggested_agent_prompt`, `source_type='lesson'`, `source_fingerprint` for dedupe), set `status: triaged`, spawn + link a `work_item`. Decided in the morning. This is the asynchronous equivalent of the attended interview and honors `.claude/rules/architectural-change-approval.md`.
184
+ - **simple** → accept, apply directly or via `rdc:fixit`, commit with `Fixes #N` or close with `rdc-lesson close` where the default branch is not the integration branch.
185
+ - **architectural with a single clear correct fix** (records an already-learned lesson — e.g. "add a gate", "encode X as a test") → accept, route through `rdc:plan` → `rdc:build` (or `rdc:fixit` if genuinely <5 files).
186
+ - **architectural and genuinely ambiguous** (multiple valid approaches, real tradeoffs) → write a `human_items` row (`item_type='decision'`, with options in `suggested_agent_prompt`, `source_type='lesson'`, `source_fingerprint` = the issue URL for dedupe), accept the issue against a linked `work_item`. Decided in the morning. This is the asynchronous equivalent of the attended interview and honors `.claude/rules/architectural-change-approval.md`.
187
+
188
+ ### Conversion backlog — remaining lesson files
189
+
190
+ List the lesson files still in the project (`.rdc/lessons/*.md`: doc-only and product lessons from before issues). They are a backlog to convert, not a place to capture: with the capacity left after the issues, pick files and encode each as a guard or test, point any live link at that artifact, and delete the file in the same commit (spec § Existing `.rdc/lessons/` files). Nothing new is written there — a new lesson, doc-only included, is an issue.
181
191
 
182
- Never delete lesson files — `applied` and `wont-fix` stay as the audit trail. Report captured / applied / escalated / deferred counts in the housekeeping report.
192
+ Issues are closed, never deleted — a closed issue with its fixing commit or won't-fix reason is the audit trail. Report open / recurred / accepted / closed-fixed / closed-won't-fix counts with issue URLs, and backlog files converted / remaining, in the housekeeping report.
183
193
 
184
194
  ## Rules
185
195
  - Never run `pnpm build` — not needed for this audit
@@ -1466,4 +1466,4 @@ Append Phase 7 results to ONRAMP-REPORT.md (final section).
1466
1466
 
1467
1467
  ## Capture Lessons
1468
1468
 
1469
- Before the final verdict, follow `.rdc/guides/lessons-learned-spec.md`. Write `.rdc/lessons/<YYYY-MM-DD>-onramp-<slug>.md` if the run taught something non-obvious.
1469
+ Before the final verdict, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure if the run taught something non-obvious. A lesson whose fix shipped as a guard, test or script this run goes in that commit's message; any other lesson is submitted to the repository the fix belongs to — `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" submit --repo <owning repo slug> --title "…" --what "…" --root-cause "…" --fix "…"` opens one `lesson` + `proposal` issue there. No lesson file is written.
@@ -47,7 +47,7 @@ Before touching any code, verify the environment is safe:
47
47
  1. **Clauth daemon alive AND unlocked:** `/ping` alive is NOT enough — a LOCKED
48
48
  vault answers `/ping` but every `/v/<service>` returns nothing, so credentials
49
49
  silently resolve empty and a long unattended run fails hours later
50
- (lesson 2026-06-16-overnight-preflight-clauth-locked-and-shared-develop-cells).
50
+ (retired lesson 2026-06-16-overnight-preflight-clauth-locked-and-shared-develop-cells).
51
51
  Assert `locked:false` before proceeding:
52
52
  ```bash
53
53
  curl -s http://127.0.0.1:52437/ping
@@ -58,8 +58,8 @@ Before touching any code, verify the environment is safe:
58
58
  2. **Git state clean AND no concurrent committer on shared develop:** Overnight
59
59
  shares the `develop` working tree with other cells/sessions. A second session
60
60
  committing concurrently can drop a just-committed file during a rebase
61
- (lessons 2026-06-16-overnight-preflight-clauth-locked-and-shared-develop-cells,
62
- 2026-06-17-build-shared-develop-rebase-dropped-committed-file).
61
+ (retired lessons 2026-06-16-overnight-preflight-clauth-locked-and-shared-develop-cells,
62
+ retired lesson 2026-06-17-build-shared-develop-rebase-dropped-committed-file).
63
63
  ```bash
64
64
  git status --short
65
65
  git fetch -q origin && git log --oneline @..@{u} # any rows = origin moved under you
@@ -144,7 +144,7 @@ After each wave: check `BUILD_STATUS`. If `escalated: true`, log the escalation
144
144
  in the overnight doc and continue — don't stop the loop.
145
145
  After each wave and after every resumed epic, require the `runOrchestrator()` receipt from `rdc:build` **where the orchestrator applies** — the target contains `corpus/_shared/build/phase-manifest.json` (today: regen-root; see `rdc:build` §1a). Elsewhere, require the contract checklist (`rdc-work check`) instead; an absent orchestrator is not a hold. `admission_refocus` or `pipeline_blocked` means the epic is held for an explicitly requested Design Review or validator closure; log that state and do not hand-reconstruct a dispatch wave.
146
146
 
147
- **Mandatory code-review gate inherited from rdc:build (Step 9b).** Every wave inside `rdc:build` runs a `pr-review-toolkit:code-reviewer` pass before the next wave dispatches. Critical/high findings reopen the affected work items to `todo` and the next wave fixes them. Overnight does not skip or weaken this gate. If a wave's code-review escalates twice, advisor decides; otherwise the loop continues.
147
+ **Mandatory code-review gate inherited from rdc:build (Step 9b).** Every wave inside `rdc:build` runs the deterministic review gate (`guides/review-gate.md`: solid-score --diff, linter on changed files, tsc, scoped tests, UI spec) before the next wave dispatches. It takes one to two minutes, where an open-ended reviewer took 4–10, which matters most across an overnight queue. A red step reopens the affected work items to `todo` and the next wave fixes them. Overnight does not skip or weaken this gate. If a wave's gate escalates twice, the advisor decides; otherwise the loop continues.
148
148
 
149
149
  ### 3d. Review
150
150
 
@@ -253,4 +253,10 @@ Provide the advisor with:
253
253
 
254
254
  ## Capture lessons (exit step)
255
255
 
256
- Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — write one `.rdc/lessons/<YYYY-MM-DD>-overnight-<short-slug>.md` per lesson using the schema in that spec. Set `scope` (`simple` | `architectural`) and `status` (`open`, or `applied` if you shipped the fix in this same run, with the commit linked). Commit the lesson file(s) on `develop` alongside the run's other commits, and note "N lessons captured" in your verdict/summary. A run that taught nothing writes nothing — absence is the default.
256
+ Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — record each lesson where its fix belongs:
257
+
258
+ - **Fix encoded in this run** (a guard, test or script shipped, commit linked): the lesson goes in that commit's message. No file, no issue.
259
+ - **Fix not encoded**: submit it to the repository the fix belongs to, which opens one `lesson` + `proposal` issue there (an identical title opens nothing):
260
+ `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" submit --repo <owning repo slug> --title "…" --what "…" --root-cause "…" --fix "…"`
261
+
262
+ No lesson file is written. Note "N lessons captured (E encoded, S submitted)" with the issue URLs in your verdict/summary. A run that taught nothing records nothing — absence is the default.
@@ -383,4 +383,10 @@ designing topology. Do not reach for the REMOTE rule by default.
383
383
 
384
384
  ## Capture lessons (exit step)
385
385
 
386
- Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — write one `.rdc/lessons/<YYYY-MM-DD>-plan-<short-slug>.md` per lesson using the schema in that spec. Set `scope` (`simple` | `architectural`) and `status` (`open`, or `applied` if you shipped the fix in this same run, with the commit linked). Commit the lesson file(s) on `develop` alongside the run's other commits, and note "N lessons captured" in your verdict/summary. A run that taught nothing writes nothing — absence is the default.
386
+ Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — record each lesson where its fix belongs:
387
+
388
+ - **Fix encoded in this run** (a guard, test or script shipped, commit linked): the lesson goes in that commit's message. No file, no issue.
389
+ - **Fix not encoded**: submit it to the repository the fix belongs to, which opens one `lesson` + `proposal` issue there (an identical title opens nothing):
390
+ `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" submit --repo <owning repo slug> --title "…" --what "…" --root-cause "…" --fix "…"`
391
+
392
+ No lesson file is written. Note "N lessons captured (E encoded, S submitted)" with the issue URLs in your verdict/summary. A run that taught nothing records nothing — absence is the default.
@@ -87,4 +87,10 @@ direction if given. If advisor cannot resolve, log and skip to next step.
87
87
 
88
88
  ## Capture lessons (exit step)
89
89
 
90
- Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — write one `.rdc/lessons/<YYYY-MM-DD>-preplan-<short-slug>.md` per lesson using the schema in that spec. Set `scope` (`simple` | `architectural`) and `status` (`open`, or `applied` if you shipped the fix in this same run, with the commit linked). Commit the lesson file(s) on `develop` alongside the run's other commits, and note "N lessons captured" in your verdict/summary. A run that taught nothing writes nothing — absence is the default.
90
+ Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — record each lesson where its fix belongs:
91
+
92
+ - **Fix encoded in this run** (a guard, test or script shipped, commit linked): the lesson goes in that commit's message. No file, no issue.
93
+ - **Fix not encoded**: submit it to the repository the fix belongs to, which opens one `lesson` + `proposal` issue there (an identical title opens nothing):
94
+ `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" submit --repo <owning repo slug> --title "…" --what "…" --root-cause "…" --fix "…"`
95
+
96
+ No lesson file is written. Note "N lessons captured (E encoded, S submitted)" with the issue URLs in your verdict/summary. A run that taught nothing records nothing — absence is the default.
@@ -41,7 +41,7 @@ rdc:release: <repo> vX.Y.Z -> vA.B.C
41
41
  [ ] Dry-run gate handled
42
42
  [ ] Version files updated
43
43
  [ ] Tests/self-test passed
44
- [ ] Mandatory release code-review (pr-review-toolkit:code-reviewer on `git diff <last-released-tag>..HEAD`). Block release on `critical`/`high` findings; record `medium`/`low` in the release notes and proceed.
44
+ [ ] Mandatory release review gate (`guides/review-gate.md`, BASE=<last-released-tag>): solid-score --diff, linter on changed files, tsc, scoped tests. Block the release on any red step; LLM reviewer only for a named question.
45
45
  [ ] Commit created
46
46
  [ ] Tag created
47
47
  [ ] Branch and tag pushed
@@ -139,12 +139,12 @@ doc-sync/scope guard to catch contamination. In them:
139
139
  - **Pre-tag guard: refuse to tag if `git diff --cached --name-only` includes any
140
140
  path outside the declared change set.** A broad add swept 4 pre-existing
141
141
  untracked skill files into a tagged release that CI published before anyone
142
- noticed (lesson 2026-06-08-release-git-add-all-swept-untracked-wip). Same
142
+ noticed (retired lesson 2026-06-08-release-git-add-all-swept-untracked-wip). Same
143
143
  dirty-tree contamination class as a lockfile generated against a dirty tree.
144
144
 
145
145
  ## ⛔ Cross-platform prepack + verify the PUBLISHED tarball
146
146
 
147
- - **Prepack must be OS-agnostic.** A bash-style `prepack` chain (`node A || true && node B || true && node stamp`) short-circuits under Windows **cmd.exe** (npm runs lifecycle scripts via cmd, not bash; `true` is not a cmd builtin and `||`/`&&` evaluate differently), so an appended step silently never runs (lesson 2026-06-13-release-windows-cmd-prepack-shortcircuit). When a prepack step must run cross-platform, use a node wrapper / `shx` / `cross-env` — never rely on `|| true` shell semantics that differ between cmd and bash.
147
+ - **Prepack must be OS-agnostic.** A bash-style `prepack` chain (`node A || true && node B || true && node stamp`) short-circuits under Windows **cmd.exe** (npm runs lifecycle scripts via cmd, not bash; `true` is not a cmd builtin and `||`/`&&` evaluate differently), so an appended step silently never runs (retired lesson 2026-06-13-release-windows-cmd-prepack-shortcircuit). When a prepack step must run cross-platform, use a node wrapper / `shx` / `cross-env` — never rely on `|| true` shell semantics that differ between cmd and bash.
148
148
  - **Validate the PUBLISHED artifact, not a local Windows `npm pack`.** A local Windows `npm pack` is NOT a faithful rehearsal of the CI (ubuntu/bash) publish. After publish, verify the real tarball:
149
149
  ```bash
150
150
  npm pack <pkg>@<version> # downloads the PUBLISHED tarball
@@ -165,4 +165,10 @@ Use `--profile lifeai` only on a workstation that intentionally has the LIFEAI p
165
165
 
166
166
  ## Capture lessons (exit step)
167
167
 
168
- Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — write one `.rdc/lessons/<YYYY-MM-DD>-release-<short-slug>.md` per lesson using the schema in that spec. Set `scope` (`simple` | `architectural`) and `status` (`open`, or `applied` if you shipped the fix in this same run, with the commit linked). Commit the lesson file(s) on `develop` alongside the run's other commits, and note "N lessons captured" in your verdict/summary. A run that taught nothing writes nothing — absence is the default.
168
+ Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — record each lesson where its fix belongs:
169
+
170
+ - **Fix encoded in this run** (a guard, test or script shipped, commit linked): the lesson goes in that commit's message. No file, no issue.
171
+ - **Fix not encoded**: submit it to the repository the fix belongs to, which opens one `lesson` + `proposal` issue there (an identical title opens nothing):
172
+ `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" submit --repo <owning repo slug> --title "…" --what "…" --root-cause "…" --fix "…"`
173
+
174
+ No lesson file is written. Note "N lessons captured (E encoded, S submitted)" with the issue URLs in your verdict/summary. A run that taught nothing records nothing — absence is the default.
@@ -12,8 +12,9 @@ description: rdc:review () - [--unattended] — post-build gate over tsc, tests,
12
12
  > **Sandbox contract:** This skill honors `RDC_TEST=1` per `guides/agent-bootstrap.md` § RDC_TEST Sandbox Contract. Destructive external calls short-circuit under the flag.
13
13
 
14
14
  > **Standard being enforced:** `docs/CODING-STANDARDS.md` (SOLID/Clean-Architecture —
15
- > regen-root; skip if absent). Step 8c is where it's actually checked, via this package's
15
+ > regen-root; skip if absent). Steps 8b and 8c check it, via this package's
16
16
  > own `rdc-solid-score` (also invoked by ATF's Test-Ladder as a consumer, not an owner).
17
+ > Step 8b runs the deterministic review gate, `guides/review-gate.md`.
17
18
 
18
19
 
19
20
  # rdc:review — Quality Gate
@@ -84,41 +85,47 @@ description: rdc:review () - [--unattended] — post-build gate over tsc, tests,
84
85
  - Never silently leave orphaned tasks
85
86
  - **⛔ Raw `UPDATE work_items SET parent_id = ...` is forbidden** — bypasses RLS and all constraint checks
86
87
 
87
- 8b. **Mandatory code-review gate — dispatch pr-review-toolkit:code-reviewer:**
88
-
89
- ⛔ **No CLEAN verdict without a code-review pass.** This is the second half of the quality gate; tsc/vitest catch type and behavior errors, code-reviewer catches logic, security, and convention drift.
90
-
91
- Dispatch ONE `pr-review-toolkit:code-reviewer` agent on the full diff under review:
92
-
93
- ```
94
- Agent({
95
- subagent_type: "pr-review-toolkit:code-reviewer",
96
- description: "rdc:review code-review pass",
97
- prompt: "Review `git diff origin/main...HEAD` on the development branch.
98
- Focus on: bugs, logic errors, security vulnerabilities, project-convention
99
- adherence (.claude/rules/*, CLAUDE.md, engineering-behavior.md).
100
- Confidence-based filtering — high-confidence findings only.
101
- Return CODE_REVIEW_COMPLETE with: { critical_count, high_count, medium_count,
102
- low_count, findings: [{severity, file:line, issue, suggested_fix}] }."
103
- })
104
- ```
105
-
106
- **Severity gate:**
107
- - `critical` or `high` → verdict cannot be CLEAN. Interactive: report and pause. Unattended: auto-fix if mechanical (rename, missing await, obvious null check), otherwise escalate via advisor and mark HAS_ISSUES.
108
- - `medium` or `low` → record in the review report's "Issues" section; verdict can still be CLEAN.
109
- - Zero findings → log `CODE_REVIEW: CLEAN` and continue.
110
-
111
- Under `RDC_TEST=1`: echo `[RDC_TEST] skipping code-review dispatch` and continue.
88
+ 8b. **Mandatory code-review gate — deterministic validators on the diff:**
89
+
90
+ ⛔ **No CLEAN verdict without a review-gate pass.** Run `guides/review-gate.md`
91
+ over the diff under review, with `BASE` set to the review's base ref and `SCOPE`
92
+ set to each modified package from step 1. Its checks are
93
+ `rdc-solid-score --diff`, the repo linter on changed files (`--write`, then
94
+ check), `tsc`, scoped tests, and the UI spec for changed routes. It finishes in
95
+ one to two minutes and gives the same answer twice.
96
+ Approved: direct operator instruction — "we need to tune our code reviews to be
97
+ specific -- if you ran the validator for solid and lint etc... it should take 1-2
98
+ minutes". 2026-09-21, Dave, in session.
99
+
100
+ Dispatch `pr-review-toolkit:code-reviewer` only for a **named question** the
101
+ validators cannot answer, such as auth bypass on a protected path or a return
102
+ figure in public copy. Scope it to the files that question touches, following
103
+ the guide's "When an LLM reviewer is still right" section. A bare "review the
104
+ diff" dispatch is retired.
105
+
106
+ **Gate outcome:**
107
+ - A red gate step, or a critical/high finding from a named-question pass →
108
+ the verdict cannot be CLEAN. Interactive: report and pause. Unattended:
109
+ fix it if the fix is mechanical (`--write` lint fixes, a missing await, an
110
+ obvious null check); otherwise escalate via the advisor and mark HAS_ISSUES.
111
+ - Medium/low findings from a named-question pass → record them in the review
112
+ report's "Issues" section. The verdict can still be CLEAN.
113
+ - All steps green → quote the `REVIEW_GATE` receipt and continue.
114
+
115
+ Under `RDC_TEST=1`: echo `[RDC_TEST] skipping review gate` and continue.
112
116
 
113
117
  8c. **Form/fit/function gate — solid-validator + architecture-reviewer:**
114
118
 
115
- ⛔ **No CLEAN verdict without this pass either.** Step 8b catches logic,
116
- security, and convention drift. This step catches a different failure
117
- shape entirely: code that is logically correct and convention-clean but
118
- architecturally wrong — the `Harness` case (a use-case orchestrator that
119
- reimplements what sibling packages exist to do, scoring fine on every
120
- generic metric while failing the one check that names the actual rule).
119
+ ⛔ **No CLEAN verdict without this pass either.** Step 8b's lint, tsc and
120
+ tests catch convention drift and broken behavior. This step catches a
121
+ different failure shape: code that is logically correct and
122
+ convention-clean but architecturally wrong. The `Harness` case is the
123
+ example: a use-case orchestrator that reimplements what sibling packages
124
+ exist to do, scores fine on every generic metric, and fails the one check
125
+ that names the actual rule.
121
126
 
127
+ Reuse the `rdc-solid-score` JSON from step 8b rather than running the scorer
128
+ twice. If 8b ran without the repo's boundary config, re-run it with the config:
122
129
  ```bash
123
130
  rdc-solid-score <modified-package-path> --diff origin/main --config <repo>/.solid-score.yml --format json
124
131
  ```
@@ -212,4 +219,10 @@ description: rdc:review () - [--unattended] — post-build gate over tsc, tests,
212
219
 
213
220
  ## Capture lessons (exit step)
214
221
 
215
- Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — write one `.rdc/lessons/<YYYY-MM-DD>-review-<short-slug>.md` per lesson using the schema in that spec. Set `scope` (`simple` | `architectural`) and `status` (`open`, or `applied` if you shipped the fix in this same run, with the commit linked). Commit the lesson file(s) on `develop` alongside the run's other commits, and note "N lessons captured" in your verdict/summary. A run that taught nothing writes nothing — absence is the default.
222
+ Before the final verdict line, follow `.rdc/guides/lessons-learned-spec.md` § Capture procedure. If this run taught something non-obvious — a first root-cause theory that turned out wrong, the documented/standard path not working, a missing gate or check that cost a round, or a surprising tool/infra behavior — record each lesson where its fix belongs:
223
+
224
+ - **Fix encoded in this run** (a guard, test or script shipped, commit linked): the lesson goes in that commit's message. No file, no issue.
225
+ - **Fix not encoded**: submit it to the repository the fix belongs to, which opens one `lesson` + `proposal` issue there (an identical title opens nothing):
226
+ `node "$LIFEAI_ENV/bin/rdc-lesson.mjs" submit --repo <owning repo slug> --title "…" --what "…" --root-cause "…" --fix "…"`
227
+
228
+ No lesson file is written. Note "N lessons captured (E encoded, S submitted)" with the issue URLs in your verdict/summary. A run that taught nothing records nothing — absence is the default.
@@ -1,6 +1,6 @@
1
1
  # RDC Skill Test Suite Matrix
2
2
 
3
- Current coverage: 36 manifests for 36 skill directories.
3
+ Current coverage: 37 manifests for 37 skill directories.
4
4
 
5
5
  The manifest layer verifies each skill can be started from a realistic caller prompt in an isolated `RDC_TEST=1` sandbox. The acceptance harness can run either Claude (`--engine claude`) or Codex (`--engine codex`) against the same manifests and records the engine stream, extracted tool calls, stdout/stderr artifacts, rendered assistant output, failures, lessons learned, and next build optimizations under `.rdc/reports/`.
6
6
 
@@ -14,6 +14,7 @@ The manifest layer verifies each skill can be started from a realistic caller pr
14
14
  | `rdc:channel-formatter` | `rdc-channel-formatter.test.json` | Long article to social content pack | `exit_code`, `stdout_contains` | Output contains, output not contains, tool-call routing |
15
15
  | `rdc:co-develop` | `rdc-co-develop.test.json` | Coordination status | `commits_made`, `exit_code`, `stdout_contains` | codevelop status, sandbox skip, no chitchat/live-send negative checks |
16
16
  | `rdc:collab` | `rdc-collab.test.json` | Claude session relay fixture | `commits_made`, `exit_code`, `stdout_contains` | chitchat/SSE session handling, sandbox skip, and no-reply/no-push negative checks |
17
+ | `rdc:convo` | `rdc-convo.test.json` | Missing-peer plan critique | `exit_code`, `stdout_contains` | Unknown participant response preserved and false agreement/completion negative checks |
17
18
  | `rdc:convert` | `rdc-convert.test.json` | Markdown-to-Word conversion fixture | `exit_code`, `stdout_contains` | build-corpus command surface, Word/Markdown flags, and no-GUI/global-install/commit checks |
18
19
  | `rdc:deploy` | `rdc-deploy.test.json` | Deployment diagnosis | `commits_made`, `exit_code`, `stdout_contains` | Read-only diagnose output and destructive deploy/DNS negative checks |
19
20
  | `rdc:edit` | `rdc-edit.test.json` | Local editor launch | `exit_code`, `stdout_contains` | Resolved target, editor host URL, and no-unknown-target negative checks |
@@ -1,7 +1,7 @@
1
1
  # Tier 2 Test Manifests
2
2
 
3
3
  Each RDC skill directory has a colocated test manifest at `skills/tests/rdc-<name>.test.json`.
4
- There are currently 36 manifests for 36 skill directories. These drive the Tier 2
4
+ There are currently 37 manifests for 37 skill directories. These drive the Tier 2
5
5
  behavioral runner and the build acceptance harness for both Claude and Codex.
6
6
 
7
7
  See `MATRIX.md` for the current skill-by-skill coverage table.
@@ -0,0 +1,15 @@
1
+ {
2
+ "manifest_version": 1,
3
+ "skill": "rdc:convo",
4
+ "description": "Sandbox discussion preserves missing peer uncertainty and does not dispatch or implement.",
5
+ "fixture": {
6
+ "prompt": "rdc:convo plan-critique Evaluate a proposal to mark CDE complete after typecheck alone. No peer response is available. Return the simulated packet and unresolved decision; do not contact peers or edit files.",
7
+ "env": { "RDC_TEST": "1" }
8
+ },
9
+ "assertions": { "exit_code": 0, "stdout_contains": ["UNKNOWN"] },
10
+ "acceptance": {
11
+ "output_contains": ["UNKNOWN", "plan-critique"],
12
+ "output_not_contains": ["Claude agreed", "CDE is complete", "validator passed"]
13
+ },
14
+ "teardown": { "reset_branch": true }
15
+ }
@@ -0,0 +1,36 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFileSync } from 'node:fs';
4
+ import { getSkill, getSkillBody, resolveSkillName, searchSkills } from '../lib/catalog.mjs';
5
+ import { toCloudBody } from '../lib/cloud-rewrite.mjs';
6
+
7
+ const names = ['outcome-exploration', 'design-comparison', 'troubleshooting', 'plan-critique', 'disagreement-resolution', 'work-handoff'];
8
+
9
+ test('convo resolves explicit invocation and ships complete templates in both caller variants', () => {
10
+ for (const alias of ['convo', 'rdc:convo', '/rdc:convo']) assert.equal(resolveSkillName(alias), 'convo');
11
+ assert.equal(resolveSkillName('convo/../../deploy'), null);
12
+ const meta = getSkill('convo');
13
+ assert.equal(meta.codeflow_required, false);
14
+ assert.deepEqual(meta.produces, ['discussion_record', 'decision_record']);
15
+ assert.ok(searchSkills('fair discussion').some(row => row.name === 'convo'));
16
+ const source = readFileSync(new URL('../skills/convo/SKILL.md', import.meta.url), 'utf8').replace(/\r\n/g, '\n');
17
+ const body = getSkillBody('convo');
18
+ assert.equal(body, source.replace(/^---\n[\s\S]*?\n---\n+/, ''));
19
+ for (const rendered of [body, toCloudBody(body)]) {
20
+ const templates = [...rendered.matchAll(/```json\n([\s\S]*?)\n```/g)].map(m => JSON.parse(m[1]));
21
+ assert.deepEqual(templates.map(t => t.template), names);
22
+ for (const template of templates) {
23
+ assert.ok(template.inputs.length >= 3, template.template);
24
+ assert.ok(template.process.length >= 2, template.template);
25
+ assert.ok(template.outputs.length >= 2, template.template);
26
+ assert.ok(template.owner && template.dissent && template.handoff);
27
+ }
28
+ assert.match(rendered, /UNKNOWN/);
29
+ assert.match(rendered, /DEFER/);
30
+ assert.match(rendered, /RDC_TEST=1/);
31
+ assert.match(rendered, /Silence is not agreement/);
32
+ assert.match(rendered, /rdc:plan/);
33
+ assert.match(rendered, /rdc:build/);
34
+ assert.doesNotMatch(rendered, /127\.0\.0\.1:52437/);
35
+ }
36
+ });
@@ -0,0 +1,103 @@
1
+ import assert from 'node:assert/strict';
2
+ import { execFileSync, spawnSync } from 'node:child_process';
3
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { dirname, join } from 'node:path';
6
+ import test from 'node:test';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ import { isAbsentAtRef } from '../../scripts/solid-score.mjs';
10
+
11
+ const SCORER = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'scripts', 'solid-score.mjs');
12
+
13
+ // ── isAbsentAtRef: git's two "not at this ref" messages, and nothing else ──
14
+
15
+ test('isAbsentAtRef: "does not exist in" (absent at ref and on disk) is absent', () => {
16
+ assert.equal(isAbsentAtRef("fatal: path 'src/a.ts' does not exist in 'origin/develop'\n"), true);
17
+ });
18
+
19
+ test('isAbsentAtRef: "exists on disk, but not in" (a NEW working-tree file) is absent', () => {
20
+ // The message every --diff over a change that adds a file produces; it was
21
+ // unmatched until 2026-09-21 and crashed the gate.
22
+ assert.equal(isAbsentAtRef("fatal: path 'apps/x/tests/ui/home.spec.ts' exists on disk, but not in 'origin/develop'\n"), true);
23
+ });
24
+
25
+ test('isAbsentAtRef: a bad ref is NOT absent — it must be rethrown', () => {
26
+ assert.equal(isAbsentAtRef("fatal: invalid object name 'no-such-ref'.\n"), false);
27
+ });
28
+
29
+ test('isAbsentAtRef: a path-form mismatch is NOT absent — it must be rethrown', () => {
30
+ assert.equal(
31
+ isAbsentAtRef("fatal: path 'src/a.ts' exists, but not 'a.ts'\nhint: Did you mean 'HEAD:src/a.ts' aka 'HEAD:./a.ts'?\n"),
32
+ false,
33
+ );
34
+ });
35
+
36
+ test('isAbsentAtRef: empty or missing stderr is NOT absent', () => {
37
+ assert.equal(isAbsentAtRef(''), false);
38
+ assert.equal(isAbsentAtRef(undefined), false);
39
+ });
40
+
41
+ // ── end to end: --diff over a change that adds a file ──
42
+
43
+ function git(cwd, ...args) {
44
+ return execFileSync('git', ['-c', 'user.email=t@example.com', '-c', 'user.name=t', '-c', 'commit.gpgsign=false', ...args], {
45
+ cwd,
46
+ encoding: 'utf8',
47
+ });
48
+ }
49
+
50
+ const CLEAN_CLASS = (name) => `export class ${name} {\n private n = 0;\n add(x: number): number {\n this.n += x;\n return this.n;\n }\n}\n`;
51
+
52
+ test('solid-score --diff scores a new file as a new unit instead of crashing', () => {
53
+ const dir = mkdtempSync(join(tmpdir(), 'solid-diff-'));
54
+ try {
55
+ git(dir, 'init', '-q');
56
+ writeFileSync(join(dir, 'a.ts'), CLEAN_CLASS('Alpha'));
57
+ git(dir, 'add', 'a.ts');
58
+ git(dir, 'commit', '-q', '-m', 'base');
59
+
60
+ // One untracked new file, and one committed-after-base new file: both are
61
+ // "exists on disk, but not in <ref>" at the base ref.
62
+ writeFileSync(join(dir, 'b.ts'), CLEAN_CLASS('Beta'));
63
+ writeFileSync(join(dir, 'c.ts'), CLEAN_CLASS('Gamma'));
64
+ git(dir, 'add', 'c.ts');
65
+ git(dir, 'commit', '-q', '-m', 'add c');
66
+
67
+ const run = spawnSync(process.execPath, [SCORER, dir, '--diff', 'HEAD~1', '--format', 'json'], { encoding: 'utf8' });
68
+ assert.equal(run.status, 0, `scorer exited ${run.status}: ${run.stderr}`);
69
+ const out = JSON.parse(run.stdout);
70
+ const scored = out.results.map((r) => r.file).sort();
71
+ assert.deepEqual(scored, ['b.ts', 'c.ts'], 'both new files are scored');
72
+ assert.deepEqual(out.regressions, [], 'clean new units are not regressions');
73
+ assert.deepEqual(out.removedFiles, [], 'nothing was deleted');
74
+ } finally {
75
+ rmSync(dir, { recursive: true, force: true });
76
+ }
77
+ });
78
+
79
+ test('solid-score --diff reports a deleted file in removedFiles instead of crashing', () => {
80
+ // A deleted file is listed by `git diff --name-only <ref>`; before
81
+ // 2026-09-21 it went to extractUnits and the gate died with ENOENT.
82
+ const dir = mkdtempSync(join(tmpdir(), 'solid-diff-del-'));
83
+ try {
84
+ git(dir, 'init', '-q');
85
+ writeFileSync(join(dir, 'a.ts'), CLEAN_CLASS('Alpha'));
86
+ writeFileSync(join(dir, 'gone.ts'), CLEAN_CLASS('Gone'));
87
+ git(dir, 'add', 'a.ts', 'gone.ts');
88
+ git(dir, 'commit', '-q', '-m', 'base');
89
+
90
+ // One deletion committed after base, one edit left uncommitted.
91
+ git(dir, 'rm', '-q', 'gone.ts');
92
+ git(dir, 'commit', '-q', '-m', 'remove gone');
93
+ writeFileSync(join(dir, 'a.ts'), `${CLEAN_CLASS('Alpha')}\nexport const touched = 1;\n`);
94
+
95
+ const run = spawnSync(process.execPath, [SCORER, dir, '--diff', 'HEAD~1', '--format', 'json'], { encoding: 'utf8' });
96
+ assert.equal(run.status, 0, `scorer exited ${run.status}: ${run.stderr}`);
97
+ const out = JSON.parse(run.stdout);
98
+ assert.deepEqual(out.removedFiles, ['gone.ts'], 'the deletion is reported, not dropped');
99
+ assert.deepEqual(out.results.map((r) => r.file), ['a.ts'], 'only the surviving changed file is scored');
100
+ } finally {
101
+ rmSync(dir, { recursive: true, force: true });
102
+ }
103
+ });
@@ -219,8 +219,17 @@ EOF
219
219
  ['heredoc with a quoted delimiter passes', 'git commit -F - <<"MSG"\nfix(a): b\nMSG', 'pass'],
220
220
  ['heredoc carrying only a UUID passes', "git commit -F - <<'EOF'\nwip\n\nWork-Item: f27ff5fa-0000-4000-8000-000000000000\nEOF", 'pass'],
221
221
  ['-m still passes', 'git commit -m "fix(x): y"', 'pass'],
222
+ // -m "$(cat <<'EOF' … EOF)" — the -m regex used to match the FIRST
223
+ // unrelated quote inside the substitution (the heredoc delimiter's own
224
+ // opening quote) and return early with literal shell text as the
225
+ // "message", never reaching the heredoc fallback below. Same defect
226
+ // class as the 2026-08-29 fix, one wrapper layer further out. Filed as
227
+ // regen-root#243.
228
+ ['-m "$(cat <<EOF …)" substitution passes', "git commit -m \"$(cat <<'EOF'\nfix(guards): correct the heredoc substitution match\n\nBody line two.\nEOF\n)\"", 'pass'],
229
+ ['-m "$(cat <<-EOF …)" dash-form substitution passes', "git commit -m \"$(cat <<-'EOF'\nfix(a): b\nEOF\n)\"", 'pass'],
222
230
  // Negative controls — reading the message is not a bypass.
223
231
  ['heredoc without a type or UUID still blocks', "git commit -F - <<'EOF'\nrandom words\nEOF", 'block'],
232
+ ['-m "$(cat <<EOF …)" without a type or UUID still blocks', "git commit -m \"$(cat <<'EOF'\nrandom words\nEOF\n)\"", 'block'],
224
233
  ['a bare commit still blocks', 'git commit', 'block'],
225
234
  // Honest about what it cannot see, rather than calling it absent.
226
235
  ['a real stdin pipe reports unreadable', 'cat msg.txt | git commit -q -F -', 'unreadable'],
@@ -1,24 +0,0 @@
1
- ---
2
- id: 2026-08-17-fixit-codex-mcp-only-skills
3
- date: "2026-08-17"
4
- skill: fixit
5
- session: codex-mcp-startup-20260817
6
- scope: simple
7
- status: applied
8
- area: skill
9
- links:
10
- commits:
11
- - 263231a
12
- memory: []
13
- work_items:
14
- - dc076f35-236b-4f8d-be3f-646a66514ba5
15
- ---
16
-
17
- ## What happened
18
- Codex's real numbered-lane startup completed MCP initialization but emitted a red skill-budget error on every launch. The machine had 36 RDC skill directories in each of the global `.codex/skills` and `.agents/skills` roots while the same 36-skill catalog was already registered through the RDC-Skills MCP endpoint.
19
-
20
- ## Root cause
21
- `scripts/install-rdc-skills.js` registered the MCP endpoint and then copied the full RDC catalog into every detected Codex skill directory. Codex loaded those duplicate surfaces together, inflating startup metadata to 113 skills. The same install also left a legacy RDC marketplace block that Codex rejected as an unsupported plugin source.
22
-
23
- ## The fix / rule
24
- Codex receives RDC skills exclusively through MCP. The installer now verifies the MCP config before purging legacy file copies, fails closed if the replacement cannot be established, removes the obsolete marketplace/plugin blocks with CRLF coverage, and verifies that live Codex skill roots contain zero RDC duplicates. Implemented in commit `263231a`.