chore: rolling promotion dev -> main - #2539
Conversation
…uncil native workflow engine
…nt (council-workflow G2)
…low now (council-workflow G3)
…ouncil-workflow G4)
feat(workflows): /council native workflow engine — lens library, install stamp, lint gate
📝 WalkthroughWalkthroughThe PR replaces the legacy council skill with a native two-mode workflow, adds a shared lens corpus and lane skills, rewires council consumers, introduces workflow linting and validation gates, updates release metadata, and documents hook-injection hardening work. ChangesCouncil workflow migration
Hook hardening record
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 SkillSpector (2.3.7)skills/architecture/SKILL.mdSkillSpector returned invalid JSON output skills/brainstorm/SKILL.mdSkillSpector returned invalid JSON output skills/code-quality/SKILL.mdSkillSpector returned invalid JSON output
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request implements the /council native workflow engine, replacing the old council skill with a unified dynamic workflow that supports both deliberation and repository auditing. It introduces seven standalone lane skills, six deliberation lens cards, an install-time stamping mechanism, and a structural linter. It also integrates the new lens library into the /review and /brainstorm skills. The reviewer's feedback focuses on improving runtime safety and robustness, suggesting the use of defensive guards, optional chaining, and fallback arrays in the workflow script to handle potentially malformed or falsy outputs, as well as using fileURLToPath in the linter script for cross-platform path resolution.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const ups = entry.response && Array.isArray(entry.response.profileUpdates) ? entry.response.profileUpdates : []; | ||
| for (const u of ups) out.push({ lane: entry.member, anchor: u.anchor, change: u.change, note: u.note }); |
There was a problem hiding this comment.
To prevent potential runtime errors, add a defensive check to ensure that each element u in ups is a valid object before accessing its properties.
const ups = entry.response && Array.isArray(entry.response.profileUpdates) ? entry.response.profileUpdates : [];
for (const u of ups) {
if (u && typeof u === 'object') {
out.push({ lane: entry.member, anchor: u.anchor, change: u.change, note: u.note });
}
}| const consensus = synth.consensus.length ? synth.consensus.map((c) => `- ${c}`).join('\n') : '- (none recorded)'; | ||
| const tensions = synth.tensions.length ? synth.tensions.map((t) => `- ${t}`).join('\n') : '- (none recorded)'; | ||
| const recs = synth.recommendations | ||
| .map((r) => `| ${r.priority} | ${r.recommendation} | ${r.rationale} | ${r.risk} |`) | ||
| .join('\n'); |
There was a problem hiding this comment.
Use optional chaining and fallback arrays to safely handle cases where synth.consensus, synth.tensions, or synth.recommendations might be undefined or null.
const consensus = synth.consensus?.length ? synth.consensus.map((c) => "- " + c).join("\n") : "- (none recorded)";
const tensions = synth.tensions?.length ? synth.tensions.map((t) => "- " + t).join("\n") : "- (none recorded)";
const recs = (synth.recommendations || [])
.map((r) => "| " + r.priority + " | " + r.recommendation + " | " + r.rationale + " | " + r.risk + " |")
.join("\n");| const laneVerdicts = synth.laneVerdicts.map((l) => `- **${l.lane}** — ${l.verdict}`).join('\n'); | ||
| const findings = synth.topFindings | ||
| .map( | ||
| (f, i) => | ||
| `${i + 1}. **[${f.severity}]** (${f.lanes.join(', ')}) ${f.summary}\n - Evidence: ${f.evidence}\n - Action: ${f.action}`, | ||
| ) | ||
| .join('\n'); | ||
| // A lane that returned nothing, or a lens that never convened, is reported "not audited" here — | ||
| // never silently dropped and never averaged in. Merge the synthesizer's partial-coverage list | ||
| // with the code-known silent lanes and unresolved lenses. | ||
| const notAuditedEntries = [ | ||
| ...synth.notFullyAudited, | ||
| ...silentRound1.map((n) => `${n} (resolved but returned nothing)`), | ||
| ...notConvened.map((n) => `${n} (lens file missing)`), | ||
| ]; |
There was a problem hiding this comment.
Add defensive guards and optional chaining when mapping over synth.laneVerdicts, synth.topFindings, and synth.notFullyAudited to prevent runtime crashes if the synthesizer output is partially malformed.
const laneVerdicts = (synth.laneVerdicts || []).map((l) => "- **" + l.lane + "** — " + l.verdict).join("\n");
const findings = (synth.topFindings || [])
.map(
(f, i) =>
(i + 1) + ". **[" + f.severity + "]** (" + (f.lanes?.join(", ") || "") + ") " + f.summary + "\n - Evidence: " + f.evidence + "\n - Action: " + f.action,
)
.join("\n");
// A lane that returned nothing, or a lens that never convened, is reported "not audited" here —
// never silently dropped and never averaged in. Merge the synthesizer's partial-coverage list
// with the code-known silent lanes and unresolved lenses.
const notAuditedEntries = [
...(synth.notFullyAudited || []),
...silentRound1.map((n) => n + " (resolved but returned nothing)"),
...notConvened.map((n) => n + " (lens file missing)"),
];| const round1 = convened.map((member, i) => ({ | ||
| member: member.name, | ||
| absPath: member.absPath, | ||
| response: round1Raw[i], | ||
| })); |
| ), | ||
| ), | ||
| ); | ||
| round2 = responded.map((self, i) => ({ member: self.member, response: round2Raw[i] })); |
| import { existsSync, readFileSync, readdirSync } from 'node:fs'; | ||
| import { join } from 'node:path'; | ||
|
|
||
| const REPO_ROOT = new URL('..', import.meta.url).pathname.replace(/\/$/, ''); |
There was a problem hiding this comment.
Use fileURLToPath from node:url for robust, cross-platform resolution of import.meta.url to avoid issues with percent-encoded characters or Windows path prefixes.
| import { existsSync, readFileSync, readdirSync } from 'node:fs'; | |
| import { join } from 'node:path'; | |
| const REPO_ROOT = new URL('..', import.meta.url).pathname.replace(/\/$/, ''); | |
| import { existsSync, readFileSync, readdirSync } from 'node:fs'; | |
| import { join } from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; | |
| const REPO_ROOT = fileURLToPath(new URL('..', import.meta.url)); |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6e94cbd1a4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| const template = fs.readFileSync(templatePath, 'utf8'); | ||
| const stamped = template.split(PLACEHOLDER).join(pluginRoot); |
There was a problem hiding this comment.
Escape the stamped plugin root as JavaScript
When this hook runs on Windows, pluginRoot contains backslashes (for example C:\Users\Alice\.claude\plugins\genie), but the raw replacement lands inside the workflow’s single-quoted LENS_ROOT string. JavaScript then treats those backslashes as escapes, so the resolver sees C:UsersAlice.claudepluginsgenie and cannot find any lens files; a username or install path containing ' would also make the stamped workflow syntactically invalid. Serialize the path into the template as a JS string literal rather than doing a raw text substitution.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 12
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.genie/brainstorms/council-workflow/DESIGN.md:
- Line 100: Update the acceptance criterion for council.js to require the
runtime-shape parser contract—an async body containing only export const meta,
top-level await/return, and no export default—instead of the contradictory “ESM
parses” requirement; preserve the existing structural checks for meta.name and
forbidden constructs.
In @.genie/brainstorms/council-workflow/DRAFT.md:
- Around line 24-29: Remove the “Plugin-shipped workflows: UNVERIFIED” qualifier
from the Native workflow facts section, replacing it with wording consistent
with the verified distribution and stamp/copy mechanism documented later in the
draft.
In @.genie/wishes/council-workflow/validate/g5-gate.sh:
- Around line 5-9: Replace the incidental output grep in the validation script
with structural parsing of package.json to verify that the check script
explicitly invokes lint:council-workflow. Retain the direct
lint:council-workflow execution as the behavioral check, and report clear
failures for missing wiring or lint errors. Update the relevant assertions in
the gate script around its package.json and bun run check checks.
In @.genie/wishes/council-workflow/WISH.md:
- Around line 174-178: Keep Group 5 marked PARTIAL and do not present the
rollout as merge-ready while the live-QA gate is unmet. Before promotion, add
both required QA evidence files containing real post-release output from
Felipe’s /council runs and verify validate/g5-gate.sh passes; otherwise
explicitly revise the merge objective and documentation to state that this
post-release QA work is non-blocking.
In @.genie/wishes/hook-injection-hardening/WISH.md:
- Around line 73-74: The plan places tests under a __tests__ directory despite
the repository’s colocated-test convention. Update the referenced audit-context
and freshness test deliverables to use colocated *.test.ts files beside their
source modules, or explicitly document an approved exception if the existing
__tests__ structure must be retained.
- Line 49: Update the acceptance criterion in the wish document to distinguish
wish-scoped validation from the overall check: state that the wish-scoped gates
pass, while the full check remains blocked by the unrelated concurrently created
file. Remove the contradictory wording that claims all gates pass while noting
bun run check exits 1.
In `@skills/architecture/SKILL.md`:
- Line 16: Update the architecture audit instructions to remain read-only by
removing the directive to persist repo-profile changes. Align them with the
audit prompt in council.js: return proposed updates as data, and leave the
single write to the workflow’s dedicated synthesis/persist stage.
In `@skills/code-quality/SKILL.md`:
- Line 16: The quality audit lane must remain read-only and must not mutate
repository state or persist .genie/repo-profile.md. Update the guidance around
“When you have enough information to act” and the persistence instruction near
the referenced profile update so it only assesses and returns proposed changes;
leave all writes to the workflow persist stage used by council.js.
In `@skills/dx-docs/SKILL.md`:
- Line 16: Keep the documentation audit lane side-effect free: update the
relevant audit logic associated with the documented default assessment behavior
so it returns repo-profile changes as data rather than persisting them. Move any
profile persistence into the dedicated single-writer stage required by the
council workflow, and ensure both referenced locations follow this contract.
In `@skills/repo-hygiene/SKILL.md`:
- Around line 16-24: Clarify the write policy in the repo-hygiene instructions:
default audits must remain read-only, with persistence as the only permitted
state change unless the invocation explicitly requests broader changes. Update
the “Recall, verify, persist” section to gate updating or deleting
`.genie/repo-profile.md` behind an explicit “persist” or “fix” request, and
replace any unqualified instruction to “act” with this boundary.
In `@skills/supply-chain/SKILL.md`:
- Around line 16-22: Clarify the default persistence behavior in the
supply-chain audit instructions: either explicitly authorize only the required
repo-profile update and cleanup as a narrowly scoped exception, or require an
explicit “persist”/“harden” request before modifying repository state. Update
the sections “Assess and report by default” and “Repo profile — recall, verify,
persist” consistently.
In `@src/lib/council-workflow-stamp.test.ts`:
- Around line 1-10: Move the test currently in council-workflow-stamp.test.ts
from src/lib to plugins/genie/scripts/council-stamp.test.ts so it is colocated
with the council-stamp.cjs implementation and discovered by directory-based test
runs; preserve the existing test contents and imports, updating only any
relative paths that become invalid.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: bad9d911-4ceb-48bf-bbff-79877099d63d
⛔ Files ignored due to path filters (2)
plugins/genie/scripts/council-stamp.cjsis excluded by!plugins/genie/scripts/**plugins/genie/scripts/smart-install.jsis excluded by!plugins/genie/scripts/**
📒 Files selected for processing (42)
.claude-plugin/marketplace.json.genie/INDEX.md.genie/brainstorms/council-workflow/DESIGN.md.genie/brainstorms/council-workflow/DRAFT.md.genie/brainstorms/skill-absorbs/DRAFT.md.genie/wishes/council-workflow/WISH.md.genie/wishes/council-workflow/validate/g1-lane-skills.sh.genie/wishes/council-workflow/validate/g2-engine.sh.genie/wishes/council-workflow/validate/g3-cutover.sh.genie/wishes/council-workflow/validate/g4-consumers.sh.genie/wishes/council-workflow/validate/g5-gate.sh.genie/wishes/hook-injection-hardening/WISH.mdbiome.jsonpackage.jsonplugins/genie/.claude-plugin/plugin.jsonplugins/genie/README.mdplugins/genie/package.jsonplugins/genie/references/lenses/deployer.mdplugins/genie/references/lenses/measurer.mdplugins/genie/references/lenses/operator.mdplugins/genie/references/lenses/questioner.mdplugins/genie/references/lenses/simplifier.mdplugins/genie/references/lenses/tracer.mdplugins/genie/workflows/council.jsscripts/council-workflow-lint.tsskills/README.mdskills/architecture/SKILL.mdskills/brainstorm/SKILL.mdskills/code-quality/SKILL.mdskills/council/SKILL.mdskills/council/members/config.mdskills/council/members/routing.mdskills/council/templates/report.mdskills/dx-docs/SKILL.mdskills/genie/SKILL.mdskills/genie/reference/lifecycle.mdskills/perf/SKILL.mdskills/qa/SKILL.mdskills/repo-hygiene/SKILL.mdskills/review/SKILL.mdskills/supply-chain/SKILL.mdsrc/lib/council-workflow-stamp.test.ts
💤 Files with no reviewable changes (4)
- skills/council/members/routing.md
- skills/council/templates/report.md
- skills/council/members/config.md
- skills/council/SKILL.md
|
|
||
| ## Success Criteria | ||
|
|
||
| - [ ] `council.js` structural lint passes: `meta.name === 'council'`, ESM parses, zero `Date.now`/`Math.random`/`new Date()`/`require`/`import`/`fs` occurrences |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Align the acceptance criterion with the runtime-shape parser.
Line 100 still requires “ESM parses”, but the workflow contract is an async body with only export const meta, top-level await/return, and no export default. Replace this with the runtime-shape parse requirement to avoid contradictory validation gates.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.genie/brainstorms/council-workflow/DESIGN.md at line 100, Update the
acceptance criterion for council.js to require the runtime-shape parser
contract—an async body containing only export const meta, top-level
await/return, and no export default—instead of the contradictory “ESM parses”
requirement; preserve the existing structural checks for meta.name and forbidden
constructs.
| ### Native workflow facts (docs fetched 2026-07-09) | ||
| - Script = `export const meta {...}` + JS body; `agent()/pipeline()/parallel()/phase()`; schema-validated structured outputs; per-agent model/effort overrides. | ||
| - Saved workflows: `.claude/workflows/` (project) or `~/.claude/workflows/` (personal) → become `/<name>` commands; accept `args` (structured, no parsing). **Plugin-shipped workflows: UNVERIFIED — background check dispatched.** | ||
| - Script has NO fs/shell access — agents do all IO. No mid-run user input. Background, resumable (same session), `/workflows` progress view, per-agent token visibility. | ||
| - Skill-instructed Workflow launch counts as explicit user opt-in (skill = thin route calling Workflow tool = legit pattern). | ||
| - No SendMessage inside workflows → Socratic round 2 must be fresh agents fed round-1 transcripts via prompt (arguably better: no continuation flakiness, resumable, parallel). |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the stale “UNVERIFIED” claim.
Lines 67–73 later state that plugin workflow distribution was verified and specify the stamp/copy mechanism. Leaving line 26 unresolved makes the rollout contract contradictory.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.genie/brainstorms/council-workflow/DRAFT.md around lines 24 - 29, Remove
the “Plugin-shipped workflows: UNVERIFIED” qualifier from the Native workflow
facts section, replacing it with wording consistent with the verified
distribution and stamp/copy mechanism documented later in the draft.
| grep -q '"lint:council-workflow"' package.json || { echo "FAIL: lint:council-workflow script missing from package.json"; exit 1; } | ||
|
|
||
| # Behavioral wiring proof: check must actually RUN the council lint, not merely coexist with it. | ||
| out="$(bun run check 2>&1)" || { echo "$out"; echo "FAIL: bun run check failed"; exit 1; } | ||
| echo "$out" | grep -q 'council-workflow' || { echo "FAIL: lint:council-workflow did not run as part of bun run check"; exit 1; } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Verify lint wiring structurally instead of grepping incidental output.
The output check can false-fail when nested script names are not printed and false-pass when another command merely logs council-workflow; it does not prove that check invokes lint:council-workflow. Parse package.json to assert the dependency, then execute the lint script explicitly for behavioral coverage.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.genie/wishes/council-workflow/validate/g5-gate.sh around lines 5 - 9,
Replace the incidental output grep in the validation script with structural
parsing of package.json to verify that the check script explicitly invokes
lint:council-workflow. Retain the direct lint:council-workflow execution as the
behavioral check, and report clear failures for missing wiring or lint errors.
Update the relevant assertions in the gate script around its package.json and
bun run check checks.
| **Acceptance Criteria:** | ||
| - [x] `bun run check` green AND its output proves `lint:council-workflow` actually ran (behavioral wiring check, not just script existence) | ||
| - [ ] Both QA evidence files present with real run output — post-release, from Felipe's own `/council` runs | ||
|
|
||
| **Status:** PARTIAL (2026-07-10) — engineering half DONE by the orchestrator inline (Felipe stopped agent dispatch for this wave): `lint:council-workflow` wired into `check` after `wishes:lint` (behavioral proof: check output shows it running, exit 0, 725 pass / 1 skip), plugin README gained the `/council` workflow section (ships/distribution/modes/requirements/override) + `workflows/` in the tree. Live-QA half USER-GATED post-release per Felipe's ruling — `validate/g5-gate.sh` correctly halts at the qa/ assertions until his `/council` runs land. Stamp path pre-validated: the shipped `council-stamp.cjs` stamped a scratchpad copy (placeholder → absolute `LENS_ROOT`, action "written"). |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Do not treat this rollout as merge-ready while the required QA gate fails.
Group 5 is still PARTIAL, its live-QA criterion is unchecked, and validate/g5-gate.sh is documented to fail. Either commit both real QA artifacts before promotion or explicitly change the merge objective so this post-release work is non-blocking.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.genie/wishes/council-workflow/WISH.md around lines 174 - 178, Keep Group 5
marked PARTIAL and do not present the rollout as merge-ready while the live-QA
gate is unmet. Before promotion, add both required QA evidence files containing
real post-release output from Felipe’s /council runs and verify
validate/g5-gate.sh passes; otherwise explicitly revise the merge objective and
documentation to state that this post-release QA work is non-blocking.
| - [x] `freshness` still emits a stale-read warning for a genuinely recent file authored by another agent (proves the `--format` parse survived the de-shell). | ||
| - [x] No `execSync(` call remains in `audit-context.ts` or `freshness.ts`; both import and use `execFileSync`. | ||
| - [x] The `core.bare` startup probe no longer appears anywhere in `src/genie.ts`. | ||
| - [ ] `bun run check` fully green — **all gates PASS for this wish**: typecheck, biome lint, knip dead-code, skills:lint, wishes:lint (this wish conforms), and `bun test` = 729 pass / 1 skip / 0 fail. Box left unchecked because `bun run check` overall still exits 1 for ONE out-of-scope reason: a concurrently-created file, `.genie/wishes/council-workflow/WISH.md`, is missing the same Complexity/Model columns. Not a regression from these changes; owned by another session. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep the acceptance status internally consistent.
This criterion is unchecked and explicitly says bun run check exits 1, so “all gates PASS for this wish” is contradictory. Reword it as “wish-scoped gates pass; the full check remains blocked by the unrelated file” to avoid recording a failing gate as passed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.genie/wishes/hook-injection-hardening/WISH.md at line 49, Update the
acceptance criterion in the wish document to distinguish wish-scoped validation
from the overall check: state that the wish-scoped gates pass, while the full
check remains blocked by the unrelated concurrently created file. Remove the
contradictory wording that claims all gates pass while noting bun run check
exits 1.
|
|
||
| ## Mandate | ||
|
|
||
| Assess and report by default. Apply changes only when the invocation explicitly asks. Never assess from reading alone when a gate exists — run it and report its actual output. Findings outside this lane (architecture judgment, test gaps, performance) get a one-line handoff to the relevant lane skill under `skills/`. When you have enough information to act, act. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Do not mutate state during a quality audit.
“Act” is ambiguous against the preceding read-only mandate, and line 24 explicitly persists .genie/repo-profile.md. This conflicts with plugins/genie/workflows/council.js, whose audit prompt forbids edits and direct profile writes. Restrict the lane to returning proposed updates; the workflow persist stage should own writes.
Also applies to: 24-24
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/code-quality/SKILL.md` at line 16, The quality audit lane must remain
read-only and must not mutate repository state or persist
.genie/repo-profile.md. Update the guidance around “When you have enough
information to act” and the persistence instruction near the referenced profile
update so it only assesses and returns proposed changes; leave all writes to the
workflow persist stage used by council.js.
|
|
||
| ## Mandate | ||
|
|
||
| Assess and report by default. Apply doc fixes only when the invocation explicitly asks — and only through the repo's documented docs workflow if it has one (submodules, docs repos, review gates). Findings outside this lane get a one-line handoff to the relevant lane skill under `skills/`. Judgments come from *using* the docs and the product, never from reading them approvingly. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Keep documentation audits side-effect free.
Persisting repo-profile changes here contradicts plugins/genie/workflows/council.js, which requires audit lanes to make no edits and return profile updates as data. Move persistence to the workflow’s dedicated single-writer stage.
Also applies to: 24-24
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/dx-docs/SKILL.md` at line 16, Keep the documentation audit lane
side-effect free: update the relevant audit logic associated with the documented
default assessment behavior so it returns repo-profile changes as data rather
than persisting them. Move any profile persistence into the dedicated
single-writer stage required by the council workflow, and ensure both referenced
locations follow this contract.
| Assess and report by default. Apply changes only when the invocation explicitly asks (e.g. "fix", "clean up", "apply"). When you spot a finding outside this lane (architecture, security, tests), name it in one line as a handoff to the relevant lane skill under `skills/` — do not investigate it yourself. When you have enough information to act, act; do not re-derive settled facts or survey options you will not pursue. | ||
|
|
||
| ## Discover the Ground Truth First | ||
|
|
||
| Never judge against generic convention when the repo states its own. Before any verdict, read what exists of: `CLAUDE.md` / `AGENTS.md`, `README`, `CONTRIBUTING`, the package manifest, `.gitignore`, git hook tooling (husky, pre-commit, commitlint or equivalents), and CI config. These define the repo's *intended* contracts — your job is to find where reality has drifted from them, and where a contract is missing entirely. Deliberate tradeoffs documented there (bot commits, generated files kept on purpose, submodule workflows) are design, not defects. | ||
|
|
||
| **Genie-framework repos**: if `.genie/` exists, its contract is: `wishes/`, `brainstorms/`, and `INDEX.md` are git-tracked; `genie.db` (and WAL/SHM siblings) must be ignored. Verify with `git check-ignore` and `git ls-files .genie/`. | ||
|
|
||
| **Repo profile — recall, verify, persist.** Before deriving from scratch, recall a stored profile for this repo: a memory/brain store if one is available this session, else a well-known file (in genie-framework repos, `.genie/repo-profile.md`). For this lane the profile records the ignore contracts, config-to-enforcement map, commit conventions, and documented tradeoffs. Recalled anchors are hypotheses, not truth — spot-check them against current code and report drift as a finding. After the audit, persist what discovery learned back to the store: update rather than duplicate, delete what proved wrong. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Make the audit write boundary explicit.
The lane says changes require an explicit request, but then tells the agent to “act” and update/delete .genie/repo-profile.md after every audit. Clarify that persistence is the sole permitted state write, or gate it behind an explicit persist/fix request; otherwise a default review can mutate tracked repository state.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/repo-hygiene/SKILL.md` around lines 16 - 24, Clarify the write policy
in the repo-hygiene instructions: default audits must remain read-only, with
persistence as the only permitted state change unless the invocation explicitly
requests broader changes. Update the “Recall, verify, persist” section to gate
updating or deleting `.genie/repo-profile.md` behind an explicit “persist” or
“fix” request, and replace any unqualified instruction to “act” with this
boundary.
| Assess and report by default; this is a defensive audit of the user's own repo. Apply hardening only when the invocation explicitly asks. Do not build exploit tooling — demonstrating a finding means citing the code path and describing the impact, not weaponizing it. Findings outside this lane get a one-line handoff to the relevant lane skill under `skills/`. When the evidence supports a conclusion, state it. | ||
|
|
||
| ## Discover the Ground Truth First | ||
|
|
||
| Enumerate before auditing. From the code, CI config, and `CLAUDE.md`/`AGENTS.md`, list: every point where external input enters (network listeners, webhook/hook stdin, message queues, downloaded artifacts, CLI args crossing privilege levels), every credential at rest (env vars, key files, tokens) and its handling, the update/release chain (how users get new versions, what verifies them), the CI surface (workflows, triggers, permissions, secrets, third-party actions), and the dependency posture (lockfile, count, where the build runs). Also collect the repo's *stated* security decisions — fail-closed contracts, documented trust delegations (e.g. "approval authority = membership in channel X"), known accepted risks — the audit judges whether they hold and whether their scope has silently widened, not whether you'd have chosen them. | ||
|
|
||
| **Repo profile — recall, verify, persist.** Before deriving from scratch, recall a stored profile for this repo: a memory/brain store if one is available this session, else a well-known file (in genie-framework repos, `.genie/repo-profile.md`). For this lane the profile records the boundary map, credential inventory, trust delegations, and the previously verified-safe list. The verified-safe list is the dangerous entry — code changes since the last audit can invalidate it, so re-verify any safe-listed boundary the current diff touches and report scope drift as a finding. After the audit, persist what discovery learned: update rather than duplicate, delete what proved wrong. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Make default audit persistence explicit.
The lane promises “assess and report by default” while requiring profile updates and deletion after every audit. Define profile persistence as an explicit, narrowly scoped exception or require an explicit persist/harden request; otherwise a security audit can modify repository state without user authorization.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/supply-chain/SKILL.md` around lines 16 - 22, Clarify the default
persistence behavior in the supply-chain audit instructions: either explicitly
authorize only the required repo-profile update and cleanup as a narrowly scoped
exception, or require an explicit “persist”/“harden” request before modifying
repository state. Update the sections “Assess and report by default” and “Repo
profile — recall, verify, persist” consistently.
| /** | ||
| * Tests for council-stamp.cjs: the install-time stamp that writes the /council | ||
| * workflow template into ~/.claude/workflows with LENS_ROOT resolved. | ||
| * | ||
| * The implementation is CommonJS (it is required from the ESM SessionStart | ||
| * hook), so we load it through createRequire. Everything runs inside a tmpdir; | ||
| * afterEach removes it, so no global state is touched. | ||
| * | ||
| * Run with: bun test src/lib/council-workflow-stamp.test.ts | ||
| */ |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Colocate this test with council-stamp.cjs.
This test targets plugins/genie/scripts/council-stamp.cjs but lives under src/lib, violating the repository’s test convention and risking omission from directory-based discovery. Move it beside the implementation, for example to plugins/genie/scripts/council-stamp.test.ts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/council-workflow-stamp.test.ts` around lines 1 - 10, Move the test
currently in council-workflow-stamp.test.ts from src/lib to
plugins/genie/scripts/council-stamp.test.ts so it is colocated with the
council-stamp.cjs implementation and discovered by directory-based test runs;
preserve the existing test contents and imports, updating only any relative
paths that become invalid.
Source: Coding guidelines
Rolling Promotion PR
Auto-maintained rolling promotion PR from
devtomain.Process:
ready-to-mergeadded when all checks passSummary by CodeRabbit
New Features
/councilworkflow with deliberation and repository-audit modes.Documentation
/council,/review, and/brainstorm.Chores