session-orchestrator 3.19.0 → 3.21.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/030-wave-execution.mdc +10 -8
- package/CHANGELOG.md +494 -0
- package/README.md +16 -11
- package/agents/analyst.md +1 -1
- package/agents/architect-reviewer.md +1 -1
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +1 -1
- package/agents/dialectic-deriver.md +1 -1
- package/agents/docs-writer.md +1 -1
- package/agents/memory-proposal-collector.md +1 -1
- package/agents/qa-strategist.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/session-reviewer.md +42 -1
- package/agents/skill-applied-judge.md +1 -1
- package/agents/test-writer.md +1 -1
- package/agents/ui-developer.md +1 -1
- package/agents/ux-evaluator.md +1 -1
- package/commands/release.md +60 -0
- package/commands/session.md +6 -2
- package/docs/USER-GUIDE.md +1 -1
- package/docs/instruction-delivery.md +350 -0
- package/docs/migration-v3.md +9 -6
- package/docs/persona-panel.md +3 -1
- package/docs/scope-collision-guard.md +167 -0
- package/docs/session-config-reference.md +1 -41
- package/docs/session-config-template.md +0 -23
- package/hooks/_lib/guard-source-loader.mjs +304 -91
- package/hooks/enforce-commands.mjs +216 -17
- package/hooks/enforce-scope.mjs +236 -12
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +11 -1
- package/hooks/on-session-end.mjs +52 -5
- package/hooks/on-session-start.mjs +7 -4
- package/hooks/on-stop.mjs +127 -12
- package/hooks/post-bash-write-verify.mjs +8 -32
- package/hooks/pre-bash-destructive-guard.mjs +146 -59
- package/hooks/pre-bash-sessions-ledger-guard.mjs +493 -66
- package/hooks/pre-task-scope-disjoint.mjs +1042 -0
- package/package.json +2 -2
- package/pi/prompts/release.md +12 -0
- package/scripts/autopilot.mjs +3 -1
- package/scripts/backfill-learnings-from-vault.mjs +967 -0
- package/scripts/emit-session.mjs +45 -40
- package/scripts/export-hw-learnings.mjs +61 -2
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/backlog-scan.mjs +106 -15
- package/scripts/lib/build-live-signals.mjs +7 -3
- package/scripts/lib/ci-status-banner.mjs +207 -23
- package/scripts/lib/command-blocker.mjs +322 -62
- package/scripts/lib/git-config-drift.mjs +471 -0
- package/scripts/lib/hardening.mjs +9 -9
- package/scripts/lib/harness-audit/categories/category6.mjs +65 -12
- package/scripts/lib/io.mjs +193 -7
- package/scripts/lib/learnings/affinity.mjs +434 -0
- package/scripts/lib/learnings/candidates.mjs +736 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +408 -53
- package/scripts/lib/learnings/judgment.mjs +782 -0
- package/scripts/lib/learnings/kebab.mjs +128 -0
- package/scripts/lib/learnings/select.mjs +704 -0
- package/scripts/lib/memory-cleanup-stamp.mjs +132 -8
- package/scripts/lib/mirror-issues-banner.mjs +266 -0
- package/scripts/lib/named-vault-resolver.mjs +105 -16
- package/scripts/lib/peer-cards/schema.mjs +6 -2
- package/scripts/lib/reconcile/emitter.mjs +107 -22
- package/scripts/lib/reconcile/engine.mjs +9 -15
- package/scripts/lib/reconcile/renderer.mjs +141 -25
- package/scripts/lib/reconcile/sanitize.mjs +518 -0
- package/scripts/lib/reconcile/writer.mjs +134 -1
- package/scripts/lib/redact-spans.mjs +89 -0
- package/scripts/lib/scope-baseline.mjs +77 -17
- package/scripts/lib/scope-gate.mjs +852 -72
- package/scripts/lib/secret-masker.mjs +262 -0
- package/scripts/lib/session-close-backfill.mjs +2 -2
- package/scripts/lib/session-lock.mjs +34 -10
- package/scripts/lib/session-record-repair.mjs +551 -0
- package/scripts/lib/session-registry.mjs +9 -1
- package/scripts/lib/session-schema/serializer.mjs +54 -0
- package/scripts/lib/session-schema.mjs +1 -0
- package/scripts/lib/session-token-rollup.mjs +68 -6
- package/scripts/lib/soul-resolve.mjs +12 -0
- package/scripts/lib/state-md/mission-status.mjs +21 -12
- package/scripts/lib/tmux-layout/telemetry.mjs +43 -10
- package/scripts/lib/tmux-layout/vcs-detector.mjs +108 -4
- package/scripts/lib/validate/check-agents.mjs +77 -5
- package/scripts/lib/validate/check-banner-parity.mjs +376 -0
- package/scripts/lib/validate/check-commands.mjs +2 -20
- package/scripts/lib/validate/check-doc-cli-commands.mjs +514 -0
- package/scripts/lib/validate/check-guard-requires-parity.mjs +1148 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +18 -0
- package/scripts/lib/validate/check-learning-provenance.mjs +511 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +188 -20
- package/scripts/lib/validate/check-rules.mjs +31 -5
- package/scripts/lib/validate/check-skills.mjs +191 -0
- package/scripts/lib/validate/check-test-git-config-target.mjs +665 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +22 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +925 -0
- package/scripts/lib/validate/check-unwired-features.mjs +757 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +965 -0
- package/scripts/lib/validate/frontmatter-block.mjs +61 -0
- package/scripts/lib/validate/tier-inference.mjs +46 -8
- package/scripts/lib/vault-mirror/namespace.mjs +146 -1
- package/scripts/lib/vault-mirror/process.mjs +264 -31
- package/scripts/lib/vault-mirror/render-sessions.mjs +115 -4
- package/scripts/lib/vault-status/board-writer.mjs +300 -56
- package/scripts/lib/vault-status/narrative-mirror.mjs +119 -5
- package/scripts/lib/vcs-repo-spec.mjs +500 -19
- package/scripts/print-applicable-rules.mjs +170 -7
- package/scripts/print-learnings-index.mjs +501 -0
- package/scripts/release.mjs +616 -61
- package/scripts/repair-invalid-sessions.mjs +209 -0
- package/scripts/site-numbers.mjs +1049 -0
- package/scripts/sweep-expired-learnings.mjs +192 -32
- package/scripts/validate-plugin.mjs +82 -0
- package/scripts/validate-wave-scope.mjs +281 -12
- package/scripts/vault-mirror.mjs +26 -1
- package/skills/_shared/monitor-patterns.md +24 -4
- package/skills/_shared/state-ownership.md +17 -0
- package/skills/brainstorm/soul.md +47 -1
- package/skills/claude-md-drift-check/SKILL.md +9 -1
- package/skills/debug/SKILL.md +4 -1
- package/skills/discovery/issue-templates.md +4 -4
- package/skills/discovery/probes-code.md +2 -2
- package/skills/discovery/probes-feature.md +6 -6
- package/skills/discovery/probes-infra.md +2 -2
- package/skills/discovery/probes-session.md +5 -5
- package/skills/dispatcher/SKILL.md +10 -1
- package/skills/evolve/SKILL.md +116 -18
- package/skills/frontmatter-guard/SKILL.md +9 -1
- package/skills/gitlab-ops/SKILL.md +54 -39
- package/skills/gitlab-portfolio/SKILL.md +10 -1
- package/skills/grill/soul.md +44 -1
- package/skills/memory-cleanup/SKILL.md +18 -5
- package/skills/npm-publish/SKILL.md +22 -50
- package/skills/persona-panel/SKILL.md +3 -1
- package/skills/plan/mode-new.md +23 -5
- package/skills/plan/soul.md +46 -3
- package/skills/repo-audit/SKILL.md +10 -1
- package/skills/session-end/SKILL.md +45 -26
- package/skills/session-end/metrics-collection.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +30 -1
- package/skills/session-end/plan-verification.md +1 -5
- package/skills/session-end/session-metrics-write.md +6 -10
- package/skills/session-plan/SKILL.md +2 -2
- package/skills/session-plan/wave-template.md +1 -1
- package/skills/session-start/SKILL.md +15 -1
- package/skills/session-start/soul.md +41 -1
- package/skills/spinout/SKILL.md +5 -1
- package/skills/sunset-review/SKILL.md +11 -1
- package/skills/tmux-layout/SKILL.md +7 -2
- package/skills/vault-mirror/SKILL.md +10 -1
- package/skills/vault-sync/SKILL.md +10 -1
- package/skills/vault-sync/validator.mjs +55 -6
- package/skills/wave-executor/SKILL.md +1 -5
- package/skills/wave-executor/wave-loop.md +77 -82
- package/scripts/lib/mission-status-schema.mjs +0 -114
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
# GitLab: query recent pipeline status
|
|
13
|
-
glab pipeline list --per-page 10
|
|
13
|
+
glab pipeline list -R <OWNER>/<REPO> --per-page 10
|
|
14
14
|
|
|
15
15
|
# GitHub: query recent workflow runs
|
|
16
|
-
gh run list --limit 10
|
|
16
|
+
gh run list -R <OWNER>/<REPO> --limit 10
|
|
17
17
|
|
|
18
18
|
# Parse output for:
|
|
19
19
|
# - Repeated failures (same pipeline failing 3+ times in a row)
|
|
@@ -55,9 +55,9 @@ Grep pattern: <claimed_addition>
|
|
|
55
55
|
git show <commit_hash> -- <relevant_files>
|
|
56
56
|
|
|
57
57
|
# "Closes #N" -> verify acceptance criteria from issue #N are met
|
|
58
|
-
gh issue view <N> --json body -q '.body'
|
|
58
|
+
gh issue view -R <OWNER>/<REPO> <N> --json body -q '.body'
|
|
59
59
|
# or
|
|
60
|
-
glab issue view <N>
|
|
60
|
+
glab issue view -R <OWNER>/<REPO> <N>
|
|
61
61
|
|
|
62
62
|
# Step 3: Cross-reference claims against actual changes
|
|
63
63
|
git diff <commit_hash>~1..<commit_hash>
|
|
@@ -83,10 +83,10 @@ Evidence: <what was found or not found>
|
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
85
|
# GitLab: list open issues sorted by last update
|
|
86
|
-
glab issue list --per-page 100 | head -50
|
|
86
|
+
glab issue list -R <OWNER>/<REPO> --per-page 100 | head -50
|
|
87
87
|
|
|
88
88
|
# GitHub: list open issues sorted by last update
|
|
89
|
-
gh issue list --limit 100 --json number,title,labels,updatedAt,assignees --jq '.[] | select(.updatedAt < "<30_days_ago_iso>")'
|
|
89
|
+
gh issue list -R <OWNER>/<REPO> --limit 100 --json number,title,labels,updatedAt,assignees --jq '.[] | select(.updatedAt < "<30_days_ago_iso>")'
|
|
90
90
|
|
|
91
91
|
# Flag:
|
|
92
92
|
# - Issues with no activity in stale-issue-days (default: 30 days)
|
|
@@ -131,7 +131,7 @@ for issue in issues:
|
|
|
131
131
|
"
|
|
132
132
|
|
|
133
133
|
# GitHub: fetch issue bodies and parse cross-references
|
|
134
|
-
gh issue list --limit 100 --json number,body --jq '.[] | {number, body}' | python3 -c "
|
|
134
|
+
gh issue list -R <OWNER>/<REPO> --limit 100 --json number,body --jq '.[] | {number, body}' | python3 -c "
|
|
135
135
|
import json, sys, re
|
|
136
136
|
for line in sys.stdin:
|
|
137
137
|
issue = json.loads(line)
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dispatcher
|
|
3
|
-
description:
|
|
3
|
+
description: >
|
|
4
|
+
Use when you want the orchestrator to pick the next repo to work on across your whole portfolio — it
|
|
5
|
+
enumerates candidate repos below the confinement root, resolves free/busy from each repo's session.lock
|
|
6
|
+
lease, ranks the FREE ones by backlog priority × staleness × readiness, recommends the single most
|
|
7
|
+
worthwhile one via AskUserQuestion, atomically claims it, and routes you to the chosen entry command.
|
|
8
|
+
Triggers: "what should I work on next", "dispatch me to a repo", "pick the next project", "run
|
|
9
|
+
/dispatcher". <example>Context: operator finished a session and wants the next-best repo across the
|
|
10
|
+
portfolio. user: "/dispatcher" assistant: "Ranked 18 free repos — top recommendation: Pencil-Designs
|
|
11
|
+
(score 4.50, 90d stale). Confirm via the picker, I'll claim its lease atomically, then route you to
|
|
12
|
+
/session deep."</example>
|
|
4
13
|
model: sonnet
|
|
5
14
|
---
|
|
6
15
|
|
package/skills/evolve/SKILL.md
CHANGED
|
@@ -211,6 +211,34 @@ For each extracted pattern, check if a learning with same `type` + `subject` alr
|
|
|
211
211
|
- **If exists:** propose confidence update (+0.15 if confirmed by new evidence, -0.2 if contradicted)
|
|
212
212
|
- **If new:** propose as new learning with confidence 0.5
|
|
213
213
|
|
|
214
|
+
This match is **exact string equality on `type` + `subject`** — it is blind to two records that say the same thing in different words, and it cannot detect a contradiction at all. The `-0.2 if contradicted` branch above has therefore had no producer since it was written. Step 3.3b is that producer.
|
|
215
|
+
|
|
216
|
+
### Step 3.3b: Relation Judgment (#1016)
|
|
217
|
+
|
|
218
|
+
> **Cadence: once per candidate.** Step 3.2b's zero-patterns check and Step 3.4's single AUQ are once-per-run; Step 3.5's write is once-per-run. This step is the only per-candidate one in Phase 3 — the pool build happens once, the judgment runs for each pattern that seeds a pool.
|
|
219
|
+
|
|
220
|
+
> **Runs in `/evolve`, never in a wave.** The pool build is O(N²) over the candidate + corpus union (~13 ms at N=100 records; the viability boundary is ~N=2000). `/evolve` is operator-invoked and off the dispatch hot path — that is the whole reason this lives here and not in `skills/wave-executor/`. Do not invoke it from a wave prompt, an inter-wave checkpoint, or a hook.
|
|
221
|
+
|
|
222
|
+
Skip this step entirely when `.orchestrator/metrics/learnings.jsonl` is absent or holds fewer than 2 entries — with no corpus there is no relation to judge.
|
|
223
|
+
|
|
224
|
+
1. **Pool.** Call `buildCandidatePools(records, { now })` from `scripts/lib/learnings/candidates.mjs`, passing the union of this run's extracted candidates and the on-disk corpus. It returns `{pools, duplicates, stats}`: `duplicates` are the exact-`learning_key` groups (already certain — no judgment needed), and each `pools[]` entry is `{seed, candidates}` where `candidates[].record` is a bounded, per-seed, non-transitive neighbour set. No clustering, no transitive closure: a neighbour of a neighbour is not a neighbour.
|
|
225
|
+
|
|
226
|
+
2. **Judge, per candidate that seeds a pool.** `buildJudgmentInput({candidate, neighbours})` then `judgeCandidate(input, { judge })`, both from `scripts/lib/learnings/judgment.mjs`. `buildJudgmentInput` returns `null` for a candidate with no usable `id` — skip that candidate, do not judge it. `judge` is the injected verdict provider: on Claude Code the coordinator reads the `input` envelope and returns the JSON object its `output_contract` field describes. There is no subagent type for this — do not dispatch one (#614: a read-only agent that must write its own sidecar never fires).
|
|
227
|
+
|
|
228
|
+
3. **Apply, through the one choke point.** `applyVerdict(verdict, effects)` is the only place a judgment may become an effect. In `/evolve` every effect handler is a *proposal recorder*, never a writer: `refine` / `supersede` / `merge` record a proposed change, and `proposeContradiction` records a contradiction pair. `applyVerdict` resolves all four handlers before invoking any of them, so an unwired handler refuses the whole batch rather than applying the decisions that happened to come first.
|
|
229
|
+
|
|
230
|
+
4. **Fail closed.** `verdict.ok === false` (any of the eight failure modes — unparseable, partial, phantom_id, self_reference, empty, timeout, enum_violation, duplicate_target) means **no relation was read**, not "no relation exists". The candidate keeps its Step 3.3 exact-match verdict and nothing about it is surfaced as a relation. Never fall back to a default decision, never repair-retry a malformed verdict, and never render an unreadable judgment to the operator — surfacing a relation IS the claim, so a voided judgment must not reach the AUQ at all.
|
|
231
|
+
|
|
232
|
+
5. **Route into the existing gate.** Every surviving decision becomes an OPTION in Step 3.4's AskUserQuestion, never an action:
|
|
233
|
+
- `contradict` → a contradiction pair, presented as its own category beside "duplicate". If the operator selects it, it feeds the `-0.2 if contradicted` branch in Step 3.3 above, applied by Step 3.5(3) — which deliberately does NOT reset `expires_at`.
|
|
234
|
+
- `supersede` / `merge` → an omit-the-loser (or replace-both-with-one) proposal. If selected, the operator's next generation simply omits those ids and Step 3.5(5) archives them — never a hand-delete. The merged record must carry both sources' provenance in its own `evidence`.
|
|
235
|
+
- `refine` → an edit proposal against the existing record's `insight` / `evidence`.
|
|
236
|
+
- `skip` / `abstain` → nothing is surfaced.
|
|
237
|
+
|
|
238
|
+
**The brandmauer holds here, unchanged (#693 FA2/FA3).** The judgment computes; it never writes. Every `.claude/rules/` write and every `learnings.jsonl` write stays behind the operator's Step 3.4 selection and Step 3.5's `--prune` invocation.
|
|
239
|
+
|
|
240
|
+
**Named ceiling (revisit trigger).** A `supersede` or `merge` executed through Step 3.5(5) is tagged `_archive_reason: "superseded"` with a `_superseded_by` tombstone **only when the two records share `type` + non-empty `subject`** — that is `pruneLearnings()`'s own consolidation pass. A cross-wording pair (the exact case this step exists to find) does not share a subject, so its loser is archived `pruned` instead: still in the corpus, still resolvable by id, but the archive record does not name its replacement. Revisit when the CLI grows per-record drop routing, or when an archive audit needs to answer "what replaced this?" for cross-wording merges.
|
|
241
|
+
|
|
214
242
|
### Step 3.4: Present Findings via AskUserQuestion
|
|
215
243
|
|
|
216
244
|
Present extracted patterns to the user for confirmation. Use AskUserQuestion with `multiSelect: true`:
|
|
@@ -244,7 +272,7 @@ If user selects "Skip all" or selects nothing, abort gracefully: "No learnings s
|
|
|
244
272
|
|
|
245
273
|
For confirmed learnings, use atomic rewrite strategy:
|
|
246
274
|
|
|
247
|
-
1. Read ALL existing lines from `.orchestrator/metrics/learnings.jsonl` (if exists) into memory. If not found, check `<state-dir>/metrics/learnings.jsonl` as a legacy fallback. If legacy data is found, it will be migrated to the v2 path on write (step
|
|
275
|
+
1. Read ALL existing lines from `.orchestrator/metrics/learnings.jsonl` (if exists) into memory. If not found, check `<state-dir>/metrics/learnings.jsonl` as a legacy fallback. If legacy data is found, it will be migrated to the v2 path on write (step 5).
|
|
248
276
|
2. Apply confidence updates for confirmed existing learnings:
|
|
249
277
|
- Increment confidence by +0.15
|
|
250
278
|
- Cap at 1.0
|
|
@@ -262,14 +290,69 @@ For confirmed learnings, use atomic rewrite strategy:
|
|
|
262
290
|
- `created_at`: current ISO 8601 date
|
|
263
291
|
- `expires_at`: preserve the candidate's derived expiry when supplied; otherwise derive from `LEARNING_TTL_DAYS[type]` via `deriveExpiresAt()` (falling back to the schema default) rather than hard-coding a 30-day horizon
|
|
264
292
|
- `file_paths` (optional): repo-relative path(s) scoping the learning to specific files/directories. Required for a learning to ever become `/reconcile`-eligible (issue #900; see `docs/rule-authoring.md` § "Learning Type-Taxonomy, TTL & Provenance Standard"). For a `fragile-file` candidate, `file_paths: [subject]` is mechanically derivable — `subject` already IS the file path.
|
|
265
|
-
5. **
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
293
|
+
5. **Write the next generation through the archive-safe pipeline — NEVER a `>` redirect (#1017).**
|
|
294
|
+
|
|
295
|
+
Steps 6–8 (prune, consolidate, rewrite) are **not prose you execute by hand**. They are
|
|
296
|
+
`pruneLearnings()` in `scripts/lib/learnings/expiry-sweep.mjs`, the same module (and the same
|
|
297
|
+
crash-safe ordering, KEEP-batch probe, and `.bak-<ISO>` snapshot) the expiry sweep uses. Until
|
|
298
|
+
#1017, this step said "write entire result back with `>`" — with no archive append at all, which
|
|
299
|
+
deleted 11 of 13 `learning-id` provenance targets referenced by rendered `.claude/rules/*.md`.
|
|
300
|
+
Do not hand-roll a `jq | ... > learnings.jsonl` pass; it bypasses every #721 safety net.
|
|
301
|
+
|
|
302
|
+
Write the full next-generation entry set (existing entries **with** the step-2/3 confidence
|
|
303
|
+
updates, **plus** the step-4 new learnings) as JSONL to a temp sidecar **via the Write tool**
|
|
304
|
+
(not a shell `>` redirect — the destructive-command guard blocks it), then invoke the
|
|
305
|
+
`--prune` subcommand of the sweep CLI:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
NEXT=".orchestrator/metrics/.learnings-next.jsonl" # written by the step above
|
|
309
|
+
node scripts/sweep-expired-learnings.mjs --prune --apply --json --entries "$NEXT" && rm -f "$NEXT"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
`--file` / `--archive` default to the canonical store + archive paths — pass them only when
|
|
313
|
+
operating on a non-default pair. The command prints ONE JSON line; capture it as `$PRUNE` and
|
|
314
|
+
report its `{scanned, kept, archived, byReason}` in the final summary. Preview first with
|
|
315
|
+
`--prune --dry-run --json` (same counts, zero writes) whenever the next generation was
|
|
316
|
+
hand-assembled.
|
|
317
|
+
|
|
318
|
+
> **This step is `/evolve`'s only store-write path.** Until #1017 the invocation lived here as
|
|
319
|
+
> an inline `node --input-type=module -e` block, which is a mechanism hiding inside prose: no
|
|
320
|
+
> `--help`, no exit-code contract, no test. Do not re-inline it, and do not hand-roll a
|
|
321
|
+
> `jq | ... > learnings.jsonl` pass — that bypasses every #721 safety net.
|
|
322
|
+
|
|
323
|
+
**Exit codes are the no-op rule.** `0` = applied (or a clean no-op). `1` = input error: the
|
|
324
|
+
sidecar is absent, carries a malformed line, or holds no records — the store and the archive
|
|
325
|
+
were **not touched**;
|
|
326
|
+
re-write the sidecar and re-run. `2` = the prune itself failed inside the lib. On any non-zero
|
|
327
|
+
exit, surface the error and stop — never retry with a shell rewrite, and never delete `$NEXT`
|
|
328
|
+
(the `&&` above already withholds the `rm`, so the assembled generation survives for a retry).
|
|
329
|
+
|
|
330
|
+
`pruneLearnings()` — the function the subcommand calls — performs steps 6 + 7 + 8 mechanically
|
|
331
|
+
and archives **every** record that
|
|
332
|
+
leaves the store, tagged with `_archived_at` + an `_archive_reason` from the closed enum
|
|
333
|
+
`expired | pruned | superseded | merged`:
|
|
334
|
+
|
|
335
|
+
- **6. Prune** — `expires_at` < now → `expired`; `confidence <= 0.0` → `pruned`.
|
|
336
|
+
- **7. Consolidate duplicates (NULL-SUBJECT SAFE)** — same `type` + non-empty `subject`: the
|
|
337
|
+
highest-confidence entry wins; each loser is archived `superseded` with a
|
|
338
|
+
`_superseded_by: <winning id>` tombstone. Entries with null/empty/missing `subject` are NEVER
|
|
339
|
+
collapsed — each is keyed by its unique `id` and always preserved (issue #284).
|
|
340
|
+
- **8. Rewrite** — via `rewriteLearnings()`: full schema validation, a `.bak-<ISO>` snapshot
|
|
341
|
+
(keep-3 rotation), then an atomic tmp+rename. Any id you drop from the temp sidecar without
|
|
342
|
+
an explicit reason is archived `pruned` automatically — the store can no longer lose a record
|
|
343
|
+
silently, whatever the next generation omits.
|
|
344
|
+
|
|
345
|
+
No `graceDays` here, deliberately: `/evolve` re-stamps `expires_at` on every reinforced learning
|
|
346
|
+
in steps 2–3 of THIS run, strictly before the prune, so an entry still expired at prune time is
|
|
347
|
+
one the analyzer just declined to reinforce. (The sweep's 14-day grace exists to protect entries
|
|
348
|
+
from being archived *before* that reinforcement pass runs — a hazard that cannot occur here.)
|
|
349
|
+
|
|
350
|
+
Report the returned `{scanned, kept, archived, byReason}` alongside the counts in the final
|
|
351
|
+
summary line. On a non-zero exit, do NOT retry with a shell rewrite — surface the error. The
|
|
352
|
+
old "read back the first line to confirm valid JSON" check is redundant here: `rewriteLearnings()`
|
|
353
|
+
round-trip-validates EVERY line before any byte reaches disk (#662), and the `malformed` guard
|
|
354
|
+
above rejects an unparseable sidecar before the store is touched at all.
|
|
355
|
+
6. **Vault mirror (conditional):** Check `$CONFIG."vault-integration".enabled` via jq. If the field is missing or `false`, skip this step entirely — skill behavior is unchanged.
|
|
273
356
|
|
|
274
357
|
If `enabled` is `true`:
|
|
275
358
|
|
|
@@ -390,20 +473,26 @@ If user selects "Boost confidence", "Reduce confidence", "Delete specific learni
|
|
|
390
473
|
|
|
391
474
|
### Step 4.4: Apply Changes
|
|
392
475
|
|
|
393
|
-
Use the same
|
|
476
|
+
Use the same archive-safe pipeline as Phase 3, Step 3.5 — **never** a hand-rolled `>` rewrite (#1017):
|
|
394
477
|
|
|
395
478
|
1. Read all lines from `learnings.jsonl`
|
|
396
479
|
2. Apply the selected operation to selected learnings:
|
|
397
480
|
- **Boost:** +0.15 confidence (cap 1.0), reset expires_at to +`learning-expiry-days`
|
|
398
481
|
- **Reduce:** -0.2 confidence
|
|
399
|
-
- **Delete:**
|
|
482
|
+
- **Delete:** omit the selected entries from the next generation — do NOT delete them by hand.
|
|
483
|
+
`pruneLearnings()` detects every **record** that left the store — reconciled by `id`, or by a
|
|
484
|
+
content fingerprint when a record carries no usable `id` — and archives it with
|
|
485
|
+
`_archive_reason: "pruned"`, so a `learning-id` referenced by a rendered rule stays resolvable.
|
|
400
486
|
- **Extend:** reset expires_at to current date + `learning-expiry-days`
|
|
401
|
-
3.
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
487
|
+
3. Steps 3–5 of the old prose (prune / consolidate / rewrite) are `pruneLearnings()` — run the
|
|
488
|
+
**exact** Step 3.5(5) invocation, writing the post-operation entry set to the `--entries`
|
|
489
|
+
sidecar. It prunes
|
|
490
|
+
(`expires_at` < now → `expired`; `confidence <= 0.0` → `pruned`), consolidates duplicates
|
|
491
|
+
(same `type` + non-empty `subject`, highest confidence wins, loser archived `superseded` with
|
|
492
|
+
`_superseded_by`; null-subject entries preserved individually per #284), and rewrites through
|
|
493
|
+
`rewriteLearnings()` with its `.bak-<ISO>` snapshot.
|
|
405
494
|
|
|
406
|
-
Report: "Updated N learnings. Total active: K."
|
|
495
|
+
Report: "Updated N learnings. Total active: K. Archived: A (<byReason>)."
|
|
407
496
|
|
|
408
497
|
---
|
|
409
498
|
|
|
@@ -534,13 +623,22 @@ Cross-reference: PRD #506 AC1-AC4 + EARS gates. Vault Integration: dialectic doe
|
|
|
534
623
|
- **ALWAYS** use uuid-v4 for new learning IDs (generate via `uuidgen` or equivalent bash command)
|
|
535
624
|
- **ALWAYS** preserve a candidate-supplied `expires_at`; otherwise derive it from `LEARNING_TTL_DAYS[type]` via `deriveExpiresAt()` rather than hard-coding `learning-expiry-days`
|
|
536
625
|
- **ALWAYS** present findings to user before writing — no silent writes
|
|
537
|
-
- **ALWAYS**
|
|
626
|
+
- **ALWAYS** route store writes through `pruneLearnings()` / `rewriteLearnings()` — never a shell
|
|
627
|
+
`>` rewrite and never an append `>>`. Those helpers own the schema validation, the `.bak-<ISO>`
|
|
628
|
+
snapshot, and the atomic tmp+rename; a hand-rolled redirect owns none of them (#721, #1017)
|
|
629
|
+
- **ALWAYS** let a removed entry land in `learnings-archive.jsonl` — a record may leave the STORE,
|
|
630
|
+
but it may never leave the CORPUS. Rendered `.claude/rules/*.md` cite `learning-id` as provenance;
|
|
631
|
+
a hard delete turns that citation into a dangling pointer (#1017 measured 11 of 13 dead)
|
|
538
632
|
- **ALWAYS** cap confidence at 1.0 — never exceed
|
|
539
633
|
|
|
540
634
|
## Anti-Patterns
|
|
541
635
|
|
|
542
636
|
- **DO NOT** write learnings without user confirmation — always present via AskUserQuestion first (on Codex CLI where AskUserQuestion is unavailable, present as a numbered Markdown list)
|
|
543
|
-
- **DO NOT** append to `learnings.jsonl`
|
|
637
|
+
- **DO NOT** append to `learnings.jsonl` with `>>`, and **DO NOT** rewrite it with `>` — call
|
|
638
|
+
`pruneLearnings()` (Step 3.5(5)); a shell redirect bypasses validation, backup, and the archive
|
|
639
|
+
- **DO NOT** hard-delete a learning. Every record that leaves the store is archived with an
|
|
640
|
+
`_archive_reason` (`expired` | `pruned` | `superseded` | `merged`) and, for the last two, a
|
|
641
|
+
`_superseded_by` / `_merged_into` tombstone naming its replacement
|
|
544
642
|
- **DO NOT** create duplicate learnings — always check type + subject match first
|
|
545
643
|
- **DO NOT** set confidence above 1.0 or forget to cap it
|
|
546
644
|
- **DO NOT** fabricate patterns — only extract from actual session data with verifiable evidence
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: frontmatter-guard
|
|
3
|
-
description:
|
|
3
|
+
description: >
|
|
4
|
+
Injects the canonical vault frontmatter schema snippet into agent prompts before any vault-write task,
|
|
5
|
+
preventing malformed YAML frontmatter in Obsidian notes. <example>Context: wave-executor is about to
|
|
6
|
+
dispatch a vault-mirror agent that writes learning notes under ~/Projects/vault/40-learnings/. user:
|
|
7
|
+
"dispatch vault-write agent" assistant: "Injecting frontmatter-guard snippet into agent prompt (vault
|
|
8
|
+
scope detected). Required fields: id, type, created, updated. Enum type:
|
|
9
|
+
note|daily|project|person|reference|idea|learning|session." <commentary>The wave-executor pre-dispatch
|
|
10
|
+
hook calls detectVaultTaskScope() — the fileScope contains /Projects/vault/40-learnings/ so the guard
|
|
11
|
+
triggers and the snippet is prepended to the agent system prompt.</commentary></example>
|
|
4
12
|
model: inherit
|
|
5
13
|
---
|
|
6
14
|
|
|
@@ -6,7 +6,15 @@ model: haiku
|
|
|
6
6
|
model-preference: sonnet
|
|
7
7
|
model-preference-codex: gpt-5.4-mini
|
|
8
8
|
model-preference-cursor: claude-sonnet-4-6
|
|
9
|
-
description:
|
|
9
|
+
description: >
|
|
10
|
+
Use this skill when performing VCS operations on GitLab or GitHub repositories — creating, updating, or
|
|
11
|
+
closing issues and MRs, applying label taxonomy, running `glab`/`gh` CLI commands, or resolving project
|
|
12
|
+
IDs dynamically. Acts as the single source of truth for CLI command syntax and label conventions;
|
|
13
|
+
consuming skills reference this rather than duplicating logic. Triggers: "create a GitLab issue", "list
|
|
14
|
+
open MRs", "apply priority label", "how do I resolve the project ID", "what's the carryover issue
|
|
15
|
+
template". <example>Context: session-end needs to file a carryover issue for an incomplete task. user:
|
|
16
|
+
"/close" assistant: "Creating carryover issue via glab with the Carryover Template from gitlab-ops —
|
|
17
|
+
labels: carryover, priority::high."</example>
|
|
10
18
|
---
|
|
11
19
|
|
|
12
20
|
# VCS Operations Reference
|
|
@@ -57,9 +65,9 @@ Never hardcode project IDs. Resolve them at runtime — and re-resolve live each
|
|
|
57
65
|
|
|
58
66
|
```bash
|
|
59
67
|
# GitLab — get numeric project ID
|
|
60
|
-
glab repo view --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])"
|
|
68
|
+
glab repo view -R <OWNER>/<REPO> --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])"
|
|
61
69
|
|
|
62
|
-
# GitHub — get owner/name identifier
|
|
70
|
+
# GitHub — get owner/name identifier (gh repo takes the repo POSITIONALLY; it rejects -R)
|
|
63
71
|
gh repo view --json nameWithOwner -q '.nameWithOwner'
|
|
64
72
|
```
|
|
65
73
|
|
|
@@ -153,33 +161,35 @@ GitHub has no native issue-blocking relation at all — the body-ordering-note f
|
|
|
153
161
|
|
|
154
162
|
## Common CLI Commands
|
|
155
163
|
|
|
164
|
+
**Directive — consult this only for a command NOT listed below; every example here already complies.** Each repo-scoped `glab`/`gh` invocation carries `-R <OWNER>/<REPO>` (`glab` also accepts `GROUP/SUBGROUP/REPO` or a full remote URL — `resolveRepoSpec()` in `scripts/lib/vcs-repo-spec.mjs` produces the right spec per platform); without the flag the target is whatever the ambient cwd remote happens to be, which is the wrong project in a sibling worktree, an `/autopilot` child, or a fork. Exactly four exceptions, each probed against the binaries: `glab api`/`gh api` (no `--repo` exists — pin the host with `--hostname` from `resolveRepoHost()` instead), `gh repo <*>` (rejects `-R`; takes the repository positionally), a `glab repo` call that already names the repository positionally, and — conditionally, not subcommand-wide — `gh pr checks|view|diff|ready|merge|comment`, where `-R` is legal ONLY alongside the `<number>|<url>|<branch>` positional: `gh pr checks -R <OWNER>/<REPO> <BRANCH>` carries the flag, while a positional-less `gh pr checks -R <OWNER>/<REPO> --watch` exits 1 with `argument required when using the` `--repo` `flag` — so name the PR or drop the flag, and never derive this from `--help`, which lists `-R` under INHERITED FLAGS with no such qualifier.
|
|
165
|
+
|
|
156
166
|
### GitLab (glab)
|
|
157
167
|
|
|
158
168
|
```bash
|
|
159
169
|
# Issues
|
|
160
|
-
glab issue list --per-page 50
|
|
161
|
-
glab issue list --label "status:ready" --per-page 10
|
|
162
|
-
glab issue list --label "priority::high" --per-page 10
|
|
163
|
-
glab issue list --closed --per-page 10
|
|
164
|
-
glab issue view <IID>
|
|
165
|
-
glab issue view <IID> --comments
|
|
166
|
-
glab issue create --title "title" --label "priority::high,status:ready"
|
|
167
|
-
glab issue update <IID> --label "status:in-progress"
|
|
168
|
-
glab issue close <IID>
|
|
169
|
-
glab issue note <IID> -m "Comment text"
|
|
170
|
+
glab issue list -R <OWNER>/<REPO> --per-page 50 # All open issues
|
|
171
|
+
glab issue list -R <OWNER>/<REPO> --label "status:ready" --per-page 10 # Ready to work on
|
|
172
|
+
glab issue list -R <OWNER>/<REPO> --label "priority::high" --per-page 10 # High priority
|
|
173
|
+
glab issue list -R <OWNER>/<REPO> --closed --per-page 10 # Recently closed
|
|
174
|
+
glab issue view -R <OWNER>/<REPO> <IID> # View issue details
|
|
175
|
+
glab issue view -R <OWNER>/<REPO> <IID> --comments # With comments
|
|
176
|
+
glab issue create -R <OWNER>/<REPO> --title "title" --label "priority::high,status:ready"
|
|
177
|
+
glab issue update -R <OWNER>/<REPO> <IID> --label "status:in-progress" # WARNING: --label REPLACES the full set — see caveat below
|
|
178
|
+
glab issue close -R <OWNER>/<REPO> <IID> # then VERIFY: re-read the issue; it must show state=closed
|
|
179
|
+
glab issue note -R <OWNER>/<REPO> <IID> -m "Comment text" # Add comment
|
|
170
180
|
|
|
171
181
|
# MRs
|
|
172
|
-
glab mr list
|
|
173
|
-
glab mr create --fill --draft
|
|
174
|
-
glab mr merge <MR_IID>
|
|
182
|
+
glab mr list -R <OWNER>/<REPO> # Open MRs
|
|
183
|
+
glab mr create -R <OWNER>/<REPO> --fill --draft # Create draft MR
|
|
184
|
+
glab mr merge -R <OWNER>/<REPO> <MR_IID> # Merge MR
|
|
175
185
|
|
|
176
186
|
# Pipelines
|
|
177
|
-
glab pipeline list --per-page 5
|
|
178
|
-
glab pipeline status <ID>
|
|
187
|
+
glab pipeline list -R <OWNER>/<REPO> --per-page 5 # Recent pipelines
|
|
188
|
+
glab pipeline status -R <OWNER>/<REPO> <ID> # Pipeline details
|
|
179
189
|
|
|
180
|
-
# API (
|
|
181
|
-
glab api "projects/$(glab repo view --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")/issues?state=opened&per_page=50"
|
|
182
|
-
glab api "projects/$(glab repo view --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")/milestones?state=active"
|
|
190
|
+
# API (no --repo exists here — the endpoint path IS the target; pin the host with --hostname)
|
|
191
|
+
glab api "projects/$(glab repo view -R <OWNER>/<REPO> --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")/issues?state=opened&per_page=50"
|
|
192
|
+
glab api "projects/$(glab repo view -R <OWNER>/<REPO> --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")/milestones?state=active"
|
|
183
193
|
```
|
|
184
194
|
|
|
185
195
|
**Label update caveat (PUT-replaces, not additive):** `glab issue update --label` (and the underlying GitLab labels API) PUT-REPLACES the entire label set — it does not add to the existing set. To change a single label you must pass the FULL desired label list, or use the dedicated add/remove operations, which are themselves unreliable across `glab` versions. Preferred safe pattern: use `--label` (adds) together with `--unlabel` (removes) on `glab issue update` when your installed `glab` version supports both; otherwise read the current labels first, compute the full new set, and PUT once. The same PUT-replace semantics apply to `glab mr update --label`.
|
|
@@ -194,27 +204,27 @@ glab api "projects/$(glab repo view --output json | python3 -c "import json,sys;
|
|
|
194
204
|
|
|
195
205
|
```bash
|
|
196
206
|
# Issues
|
|
197
|
-
gh issue list --limit 50
|
|
198
|
-
gh issue list --label "status:ready" --limit 10
|
|
199
|
-
gh issue list --label "priority::high" --limit 10
|
|
200
|
-
gh issue list --state closed --limit 10
|
|
201
|
-
gh issue view <NUMBER>
|
|
202
|
-
gh issue view <NUMBER> --comments
|
|
203
|
-
gh issue create --title "title" --label "priority::high,status:ready"
|
|
204
|
-
gh issue edit <NUMBER> --add-label "status:in-progress"
|
|
205
|
-
gh issue close <NUMBER>
|
|
206
|
-
gh issue comment <NUMBER> --body "Comment text"
|
|
207
|
+
gh issue list -R <OWNER>/<REPO> --limit 50 # All open issues
|
|
208
|
+
gh issue list -R <OWNER>/<REPO> --label "status:ready" --limit 10 # Ready to work on
|
|
209
|
+
gh issue list -R <OWNER>/<REPO> --label "priority::high" --limit 10 # High priority
|
|
210
|
+
gh issue list -R <OWNER>/<REPO> --state closed --limit 10 # Recently closed
|
|
211
|
+
gh issue view -R <OWNER>/<REPO> <NUMBER> # View issue details
|
|
212
|
+
gh issue view -R <OWNER>/<REPO> <NUMBER> --comments # With comments
|
|
213
|
+
gh issue create -R <OWNER>/<REPO> --title "title" --label "priority::high,status:ready"
|
|
214
|
+
gh issue edit -R <OWNER>/<REPO> <NUMBER> --add-label "status:in-progress"
|
|
215
|
+
gh issue close -R <OWNER>/<REPO> <NUMBER>
|
|
216
|
+
gh issue comment -R <OWNER>/<REPO> <NUMBER> --body "Comment text" # Add comment
|
|
207
217
|
|
|
208
218
|
# PRs
|
|
209
|
-
gh pr list --state open
|
|
210
|
-
gh pr create --fill --draft
|
|
211
|
-
gh pr merge <NUMBER>
|
|
219
|
+
gh pr list -R <OWNER>/<REPO> --state open # Open PRs
|
|
220
|
+
gh pr create -R <OWNER>/<REPO> --fill --draft # Create draft PR
|
|
221
|
+
gh pr merge -R <OWNER>/<REPO> <NUMBER> # Merge PR
|
|
212
222
|
|
|
213
223
|
# Workflows (CI equivalent)
|
|
214
|
-
gh run list --limit 5
|
|
215
|
-
gh run view <RUN_ID>
|
|
224
|
+
gh run list -R <OWNER>/<REPO> --limit 5 # Recent workflow runs
|
|
225
|
+
gh run view -R <OWNER>/<REPO> <RUN_ID> # Run details
|
|
216
226
|
|
|
217
|
-
# API
|
|
227
|
+
# API (no --repo exists here — the endpoint path IS the target; pin the host with --hostname)
|
|
218
228
|
gh api "repos/{owner}/{repo}/issues?state=open&per_page=50"
|
|
219
229
|
gh api "repos/{owner}/{repo}/milestones?state=open"
|
|
220
230
|
```
|
|
@@ -265,6 +275,9 @@ What should be achieved and why.
|
|
|
265
275
|
### Context for next session
|
|
266
276
|
[relevant context, file paths, decisions made]
|
|
267
277
|
|
|
278
|
+
### Revisit-Trigger
|
|
279
|
+
[the concrete condition or event that reopens this — e.g. "when <metric/state> passes <threshold>", "at the next <session type/release>". A deferral with no named trigger is not a deferral — never a bare "later"/"low prio"/"TBD".]
|
|
280
|
+
|
|
268
281
|
### Open Questions
|
|
269
282
|
_(optional — include only when unanswered questions remain in STATE.md `## Open Questions` at close; omit this section entirely otherwise)_
|
|
270
283
|
- [ ] [unanswered question 1] (source: W<N>/<agent>, prio: high|medium|low)
|
|
@@ -274,6 +287,8 @@ _(optional — include only when unanswered questions remain in STATE.md `## Ope
|
|
|
274
287
|
Relates to #ORIGINAL_IID
|
|
275
288
|
```
|
|
276
289
|
|
|
290
|
+
`### Revisit-Trigger` is **mandatory** for the `/close` carryover template above: a carryover deferred without a concrete, checkable reopen condition is a rot risk — "later" reliably means "never". (The SPIRAL/FAILED escalation carryover built by `scripts/lib/spiral-carryover.mjs` is a deliberately separate, machine-triaged template and carries no trigger field.)
|
|
291
|
+
|
|
277
292
|
### Discovery Finding
|
|
278
293
|
|
|
279
294
|
```markdown
|
|
@@ -362,8 +377,8 @@ Read .gitlab/merge_request_templates/Default.md
|
|
|
362
377
|
Read .github/PULL_REQUEST_TEMPLATE.md
|
|
363
378
|
|
|
364
379
|
# 2. Then create — hook now passes
|
|
365
|
-
glab mr create --title "..." --description "..."
|
|
366
|
-
gh pr create --title "..." --body "..."
|
|
380
|
+
glab mr create -R <OWNER>/<REPO> --title "..." --description "..."
|
|
381
|
+
gh pr create -R <OWNER>/<REPO> --title "..." --body "..."
|
|
367
382
|
```
|
|
368
383
|
|
|
369
384
|
### Cross-References
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gitlab-portfolio
|
|
3
|
-
description:
|
|
3
|
+
description: >
|
|
4
|
+
Use when you need a single-pane cross-repo health view across all vault-registered GitLab and GitHub
|
|
5
|
+
projects. Discovers repos from `_overview.md` frontmatter in `<vault>/01-projects/*/`, aggregates open
|
|
6
|
+
issues, MRs, critical labels, and stale signals via parallel `glab`/`gh` calls, then writes an
|
|
7
|
+
idempotent `_PORTFOLIO.md` dashboard. Runs automatically at session-start Phase 2 when
|
|
8
|
+
`gitlab-portfolio.enabled=true`. Triggers: "show portfolio status", "refresh the portfolio dashboard",
|
|
9
|
+
"which repos have critical issues", "run /portfolio". <example>Context: session-start,
|
|
10
|
+
gitlab-portfolio.enabled=true, vault has 5 registered repos. user: "/session deep" assistant:
|
|
11
|
+
"Portfolio: 3 critical issues across 2 repos — run /portfolio for details. Dashboard written to
|
|
12
|
+
vault/01-projects/_PORTFOLIO.md."</example>
|
|
4
13
|
model: sonnet
|
|
5
14
|
---
|
|
6
15
|
|
package/skills/grill/soul.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
You are the Interrogator — a staff engineer who pressure-tests a plan, design, or PRD by playing devil's advocate. Where `/brainstorm` is a cooperative Design Facilitator that *narrows* an ambiguous design space, you are the adversarial stress test that tries to *break* a plan the user already believes in. You don't collect wishes; you hunt contradictions. You don't expand scope; you expose the assumptions hiding inside it.
|
|
6
6
|
|
|
7
|
-
You
|
|
7
|
+
You answer in the operator's language: `owner.language` in `~/.config/session-orchestrator/owner.yaml`, falling back to `en` when that file is missing, unreadable, or the key is absent — and following the operator's own language the moment he writes in another one. You meet people at their abstraction level — product language with stakeholders, interface and data-model language with engineers.
|
|
8
8
|
|
|
9
9
|
The user invited the grilling. Relentlessness is the service they asked for, not rudeness. Be sharp, be specific, never be a yes-man — but every challenge points at the plan, never at the person.
|
|
10
10
|
|
|
@@ -40,6 +40,49 @@ Apply these continuously throughout the grill — they are the substance of the
|
|
|
40
40
|
|
|
41
41
|
**Apply the tactics that bite.** Not every tactic fits every target — a tooling or meta plan may have no glossary to collide with, a greenfield idea may have no code to contradict yet. Run the tactics that have real material; never manufacture a conflict to tick a box. A forced question violates the fewer-sharper-questions discipline as surely as a skipped real one does.
|
|
42
42
|
|
|
43
|
+
## Output Levels
|
|
44
|
+
|
|
45
|
+
The active level is `efficiency.output-level` in `~/.config/session-orchestrator/owner.yaml`. If that file is missing, unreadable, or the key is absent, the level is `full`. Apply the matching block below for the whole grill.
|
|
46
|
+
|
|
47
|
+
**How to read a budget.** A *turn* is every chat line you author between the user's last answer and your next question — the evidence you read out of the code, the contradiction you name, the one sentence of recommendation reasoning. Raw Grep/Read output does not count; your narration of it does. A budget is a ceiling, not a target: under is fine, over is a defect. You meet it by WITHHOLDING, never by dropping — no contradiction disappears, it just gets stated in fewer words.
|
|
48
|
+
|
|
49
|
+
This is the tightest of the orchestrator's budgets by design. A turn is structurally one sentence plus one question; a budget generous enough to hold five questions would license exactly the volley § One question at a time forbids. The grill summary file, if the user asks for one, carries no budget — it is the artifact, not the conversation.
|
|
50
|
+
|
|
51
|
+
**Escalation (all levels).** When the operator writes `expand <topic>` (German: `mehr zu <Abschnitt>`), print that topic's full detail immediately, without re-asking and without the budget applying to that one response.
|
|
52
|
+
|
|
53
|
+
**Never traded for brevity (all levels).** No budget may be met by cutting any of the following. Where a budget and one of them collide, the budget yields:
|
|
54
|
+
- input validation, and the reporting of invalid input;
|
|
55
|
+
- error handling, error messages, and failure disclosure — a swallowed error is never "concise";
|
|
56
|
+
- security findings, warnings, and destructive-action confirmations (PSA-003);
|
|
57
|
+
- accessibility of the output itself — no meaning carried by colour or emoji alone, no bare unlabelled numbers, no table whose header you dropped to save a line;
|
|
58
|
+
- anything the operator explicitly asked to see;
|
|
59
|
+
- a kill assumption's four fields — *Fails-if*, *Evidence-this-week*, *Kill-criterion*, *Cheapest-test* (Tactic 5) — and the Tiger / Paper Tiger / Elephant sort (Tactic 6). Those are the findings themselves, not narration about them; a three-field workup is an incomplete answer, not a short one.
|
|
60
|
+
|
|
61
|
+
### output-level: ultra
|
|
62
|
+
- Meaning: telegraphic — the evidence, the contradiction, the question. No narration.
|
|
63
|
+
- Budget: ≤6 lines per turn; ≤1 line per tactic finding; ≤1 line of recommendation reasoning before the tool call. A kill-assumption or pre-mortem turn is exempt (see the never-traded list) but stays at one line per field.
|
|
64
|
+
- Shape: quote the code as `<file>:<line> — <what it does>`, then the collision, then the question. Never restate the user's answer back at them.
|
|
65
|
+
- Escalation: `expand <topic>` — see § Escalation above.
|
|
66
|
+
|
|
67
|
+
### output-level: full
|
|
68
|
+
- Meaning: terse but complete — framing trimmed, evidence preserved. This is the default.
|
|
69
|
+
- Budget: ≤14 lines per turn; ≤3 lines per tactic finding; ≤2 lines of recommendation reasoning before the tool call.
|
|
70
|
+
- Shape: the steelman in one line, then the attack, then the question. Every claim about behaviour keeps its `<file>:<line>` — that citation IS the evidence; what gets trimmed is the commentary on it.
|
|
71
|
+
- Escalation: `expand <topic>` — see § Escalation above.
|
|
72
|
+
|
|
73
|
+
### output-level: lite
|
|
74
|
+
- Meaning: verbose — the reasoning behind each challenge is spelled out. Chosen for learning, not for speed.
|
|
75
|
+
- Budget: ≤35 lines per turn; ≤8 lines per tactic finding. Still a ceiling — `lite` is not "unbounded".
|
|
76
|
+
- Shape: explain which tactic you are applying and why it bites here, name the branches of the decision tree you are deferring, define unfamiliar terms on first use.
|
|
77
|
+
- Escalation: `expand <topic>` — see § Escalation above.
|
|
78
|
+
|
|
79
|
+
### Companion dials
|
|
80
|
+
|
|
81
|
+
Same file, same lookup, same fallback-to-default rule:
|
|
82
|
+
|
|
83
|
+
- `efficiency.preamble` — `minimal` (default): at most one clause before a tool call, and only when the next step is non-obvious; never "Let me read the model." immediately followed by reading it. `verbose`: one sentence before each Grep/Read naming the contradiction you expect to find.
|
|
84
|
+
- `tone.style` — `direct` (this soul's baseline: name the contradiction plainly and make the user resolve it), `neutral` (state the collision without advocacy; still recommend when asked), `friendly` (same content, softer framing; never softer facts). No setting makes you a yes-man — the challenge always lands, only its wording moves.
|
|
85
|
+
|
|
43
86
|
## Values
|
|
44
87
|
|
|
45
88
|
- **Skeptical by default** — a plausible claim is not a verified one; the cheap challenge now saves the expensive correction later
|
|
@@ -296,11 +296,24 @@ After completing all four phases, report:
|
|
|
296
296
|
|
|
297
297
|
This advances the auto-dream cadence marker (`readDreamSignals` → `lastCleanupAt` in `scripts/lib/auto-dream.mjs`) so `shouldDispatchAutoDream` does not fire a false nudge on the next session.
|
|
298
298
|
|
|
299
|
-
**Signalling contract
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
299
|
+
**Signalling contract — emit the event, do not rely on remembering.**
|
|
300
|
+
|
|
301
|
+
As the LAST step of every completed run — dry-run, apply-pending, or healthy no-op alike — emit the completion event. This is mandatory and it is the whole mechanism; there is no second, prose-only path that also works:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
node scripts/emit-event.mjs \
|
|
305
|
+
--type orchestrator.memory.cleanup_completed \
|
|
306
|
+
--payload "{\"semantic_session_id\":\"<the session: value from STATE.md frontmatter>\",\"mode\":\"<dry-run|apply-pending|no-op>\"}"
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
`scripts/emit-session.mjs` then DERIVES `memory_cleanup_at` from that event at session-close time via `deriveMemoryCleanupSignal()` (`scripts/lib/memory-cleanup-stamp.mjs`), matching events whose `timestamp` falls inside the session's own `[started_at, completed_at]` window. Nothing downstream depends on the coordinator recalling that a cleanup happened.
|
|
310
|
+
|
|
311
|
+
- **Do NOT distinguish "applied changes" from "healthy no-op"** — both count as a run, so both emit. Put the distinction in `mode`, never in whether you emit.
|
|
312
|
+
- **Do NOT hand-append to `events.jsonl`.** Route through `emit-event.mjs` → `emitEvent()`; hand-rolled appenders drift from the canonical record shape (the `stop` vs `orchestrator.session.stopped` divergence, #609).
|
|
313
|
+
- **`semantic_session_id` is the semantic id** (`main-2026-08-17-session-1`), not the UUID — `sessions.jsonl` `session_id` lives in that same space, and the matcher compares against it. Omitting the field is tolerated (the event is then claimed on the time window alone), but supplying it is what makes attribution exact when two sessions overlap.
|
|
314
|
+
- **An explicit `memory_cleanup_at` already on the record WINS** over derivation and is never overwritten. That path exists for backfills and tests, not for normal operation.
|
|
315
|
+
|
|
316
|
+
**Why this is mechanical and not a prose instruction:** it used to be one. On 2026-08-14 a `/memory-cleanup` ran and produced a documented yield, the coordinator did not execute the prose step, and all three session records of that day carried `memory_cleanup_at: null` — so the session-start banner reported "last cleanup 29 days ago" while the operator's own notes said 3. `stampMemoryCleanup()` had zero production callers at the time; every reference to it was an instruction asking an LLM to remember. Same failure class as the STATE.md write-race that Epic #583 replaced with a lock: Disziplin statt Mechanik.
|
|
304
317
|
|
|
305
318
|
## Anti-Patterns
|
|
306
319
|
|