session-orchestrator 3.20.0 → 3.22.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/000-session-orchestrator.mdc +3 -2
- package/.cursor/rules/030-wave-execution.mdc +10 -8
- package/.cursor/rules/040-discovery.mdc +6 -6
- package/.cursor/rules/050-plan.mdc +8 -8
- package/CHANGELOG.md +515 -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 +7 -5
- 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/eli5.md +33 -0
- package/commands/release.md +62 -0
- package/commands/test.md +2 -2
- package/docs/components.md +6 -5
- 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 +31 -8
- package/hooks/_lib/lock-bootstrap.mjs +19 -13
- package/hooks/enforce-scope.mjs +103 -3
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +21 -1
- package/hooks/on-session-end.mjs +76 -97
- package/hooks/on-session-start.mjs +195 -104
- package/hooks/on-stop.mjs +127 -12
- package/hooks/post-bash-write-verify.mjs +8 -32
- package/hooks/pre-auq-clarity.mjs +787 -0
- package/hooks/pre-bash-issue-budget.mjs +17 -18
- package/hooks/pre-task-scope-disjoint.mjs +1042 -0
- package/package.json +3 -1
- package/pi/prompts/eli5.md +12 -0
- package/pi/prompts/release.md +12 -0
- package/scripts/auq-audit.mjs +825 -0
- package/scripts/autopilot.mjs +10 -9
- package/scripts/emit-session.mjs +42 -0
- package/scripts/export-hw-learnings.mjs +61 -2
- package/scripts/lib/auq/clarity.mjs +1314 -0
- package/scripts/lib/auq/parse.mjs +1006 -0
- package/scripts/lib/auq/schema.mjs +1457 -0
- 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 +267 -77
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +32 -9
- package/scripts/lib/config/vault-integration.mjs +12 -1
- package/scripts/lib/dispatcher/rank.mjs +4 -7
- package/scripts/lib/gates/gate-full.mjs +3 -3
- package/scripts/lib/gates/gate-helpers.mjs +17 -6
- package/scripts/lib/git-config-drift.mjs +471 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +65 -12
- package/scripts/lib/io.mjs +432 -7
- package/scripts/lib/issue-budget.mjs +63 -9
- package/scripts/lib/learnings/select.mjs +157 -3
- 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/owner-interview.mjs +78 -32
- package/scripts/lib/peer-cards/schema.mjs +6 -2
- package/scripts/lib/peer-discovery.mjs +73 -22
- package/scripts/lib/project-hygiene.mjs +64 -4
- package/scripts/lib/reconcile/renderer.mjs +17 -4
- package/scripts/lib/reconcile/writer.mjs +69 -30
- package/scripts/lib/redact-spans.mjs +89 -0
- package/scripts/lib/resource-probe/evaluate.mjs +330 -149
- package/scripts/lib/resource-probe/probe-platform.mjs +35 -0
- package/scripts/lib/resource-probe.mjs +18 -2
- package/scripts/lib/scope-baseline.mjs +77 -17
- package/scripts/lib/scope-gate.mjs +658 -0
- package/scripts/lib/secret-masker.mjs +262 -0
- package/scripts/lib/session-lock.mjs +34 -10
- package/scripts/lib/session-registry.mjs +9 -1
- package/scripts/lib/spiral-carryover.mjs +23 -2
- package/scripts/lib/state-md/mission-status.mjs +164 -58
- package/scripts/lib/tmux-layout/vcs-detector.mjs +108 -4
- package/scripts/lib/validate/check-agents.mjs +77 -5
- package/scripts/lib/validate/check-auq-clarity.mjs +274 -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-hooks-symmetry.mjs +48 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +185 -17
- package/scripts/lib/validate/check-rules.mjs +153 -9
- 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 +219 -11
- 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-backfill/glab.mjs +91 -58
- package/scripts/lib/vault-backfill/manifest.mjs +28 -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 +680 -30
- package/scripts/lib/wave-resource-gate.mjs +67 -73
- package/scripts/materialize-wave-scope.mjs +281 -0
- package/scripts/print-learnings-index.mjs +30 -3
- package/scripts/release.mjs +983 -107
- package/scripts/run-quality-gate.mjs +14 -0
- package/scripts/site-numbers.mjs +1049 -0
- package/scripts/validate-plugin.mjs +64 -0
- package/scripts/validate-wave-scope.mjs +286 -12
- package/scripts/vault-backfill.mjs +32 -5
- package/scripts/vault-mirror.mjs +26 -1
- package/skills/_shared/monitor-patterns.md +24 -4
- package/skills/_shared/parallel-aware-auq.md +30 -24
- package/skills/_shared/parallel-aware-preamble.md +31 -2
- package/skills/_shared/state-ownership.md +49 -6
- package/skills/bootstrap/SKILL.md +2 -1
- package/skills/brainstorm/SKILL.md +18 -18
- package/skills/brainstorm/soul.md +12 -0
- package/skills/claude-md-drift-check/SKILL.md +9 -1
- package/skills/debug/SKILL.md +4 -1
- package/skills/discovery/SKILL.md +28 -24
- 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/eli5/SKILL.md +43 -0
- package/skills/evolve/SKILL.md +8 -9
- package/skills/frontmatter-guard/SKILL.md +9 -1
- package/skills/gitlab-ops/SKILL.md +73 -59
- package/skills/gitlab-portfolio/SKILL.md +10 -1
- package/skills/grill/SKILL.md +6 -6
- package/skills/grill/soul.md +16 -0
- package/skills/memory-cleanup/SKILL.md +20 -7
- package/skills/npm-publish/SKILL.md +23 -51
- package/skills/peekaboo-driver/SKILL.md +3 -3
- package/skills/persona-panel/SKILL.md +3 -1
- package/skills/plan/SKILL.md +18 -16
- package/skills/plan/mode-feature.md +1 -1
- package/skills/plan/mode-new.md +42 -12
- package/skills/plan/soul.md +12 -0
- package/skills/reconcile/SKILL.md +3 -3
- package/skills/repo-audit/SKILL.md +10 -1
- package/skills/session-end/SKILL.md +97 -22
- package/skills/session-end/metrics-collection.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +37 -2
- package/skills/session-end/session-metrics-write.md +4 -10
- package/skills/session-plan/SKILL.md +2 -2
- package/skills/session-plan/wave-template.md +1 -1
- package/skills/session-start/SKILL.md +82 -36
- package/skills/session-start/phase-2-5-docs-planning.md +8 -8
- package/skills/session-start/phase-4-5-resource-health.md +82 -19
- package/skills/session-start/soul.md +110 -0
- package/skills/spinout/SKILL.md +5 -1
- package/skills/sunset-review/SKILL.md +11 -1
- package/skills/test-runner/SKILL.md +2 -2
- package/skills/tmux-layout/SKILL.md +7 -2
- package/skills/using-orchestrator/SKILL.md +1 -1
- 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/wave-loop.md +64 -12
- package/skills/write-executable-plan/SKILL.md +6 -6
- package/scripts/lib/mission-status-schema.mjs +0 -114
- package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +0 -8
- package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +0 -8
- package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +0 -8
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +0 -8
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +0 -10
- package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/README.md +0 -3
- package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +0 -11
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +0 -9
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +0 -8
- package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +0 -7
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +0 -9
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +0 -11
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +0 -3
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +0 -3
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +0 -11
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +0 -11
- package/skills/vault-sync/tests/schema-drift.test.mjs +0 -133
|
@@ -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
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eli5
|
|
3
|
+
description: Explain a topic like I'm a 5 year old — restate my last output, or a named topic, in plain words without dropping a single fact. Use when the user types /eli5 [topic], or says an answer was too technical, too long, or unclear about what he now has to do.
|
|
4
|
+
model: inherit
|
|
5
|
+
tools: Read, Grep, Glob, Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# eli5
|
|
9
|
+
|
|
10
|
+
Say it again in plain words. Same facts, in the order he needs them.
|
|
11
|
+
|
|
12
|
+
## The frame
|
|
13
|
+
|
|
14
|
+
**Write for someone who knows this project but has not seen what you just saw.**
|
|
15
|
+
|
|
16
|
+
He owns this repo. He is not missing knowledge, he is missing observation — he did not watch the command run or read the file it points at. Write across, not down. `.claude/rules/ask-via-tool.md` § AUQ-006 and `skills/session-start/soul.md` § Register are the canonical statement of this register; apply them, do not restate them.
|
|
17
|
+
|
|
18
|
+
## What to restate
|
|
19
|
+
|
|
20
|
+
- `/eli5` — my last substantial output in this conversation. If there is none yet, say so instead of inventing a topic.
|
|
21
|
+
- `/eli5 <topic>` — that topic (`$ARGUMENTS`), grounded in what THIS session already measured: name the file, command, or record it came from. If it was never measured here, say that rather than filling the gap from memory.
|
|
22
|
+
|
|
23
|
+
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 follow the operator's own language the moment he writes in another one.
|
|
24
|
+
|
|
25
|
+
## Two limits, and they are the whole skill
|
|
26
|
+
|
|
27
|
+
**1. Say more simply what actually happens — introduce nothing that does not exist.**
|
|
28
|
+
Test: delete every noun the system does not contain. Sentence still true and complete → it was no analogy. Sentence collapses → describe what actually happens instead.
|
|
29
|
+
✓ "Waiting means the other session finishes first." ✗ "Think of the session as a level crossing."
|
|
30
|
+
|
|
31
|
+
**2. Simplifying removes words, never facts.** If a path, a number, an error code, an identifier, or an instruction to act disappears, that is data loss, not simplification. The mechanical decider: **could the token you are about to cut ever appear in a `grep`? Then it stays.** `skills/session-start/soul.md` § "Never traded for brevity" outranks every brevity instruction in this file.
|
|
32
|
+
|
|
33
|
+
## Shape
|
|
34
|
+
|
|
35
|
+
Line 1 answers the question he actually has: **do I have to do something now, and what happens if I don't?** Then the facts, in the order he needs them — not the order you found them.
|
|
36
|
+
|
|
37
|
+
This is a terminal, so there is no picture to draw. The second channel is the `preview` field on an `AskUserQuestion` option: when the options differ in something literal — a diff, a title, a config block, a file list — put that text there and he reads the thing instead of a description of it.
|
|
38
|
+
|
|
39
|
+
## What this is not
|
|
40
|
+
|
|
41
|
+
- **Not shorter by default.** Eight lines before may be eight lines after. Reorder first; cut only filler.
|
|
42
|
+
- **Not a children's explanation.** The label is a poster, the reader is an expert who was not in the room.
|
|
43
|
+
- **Not a second attempt at the answer.** If the first answer was wrong, fix the answer — `/eli5` restates, it never re-derives.
|
package/skills/evolve/SKILL.md
CHANGED
|
@@ -248,8 +248,8 @@ Present extracted patterns to the user for confirmation. Use AskUserQuestion wit
|
|
|
248
248
|
```
|
|
249
249
|
AskUserQuestion({
|
|
250
250
|
questions: [{
|
|
251
|
-
question: "Which
|
|
252
|
-
header: "
|
|
251
|
+
question: "Which of the patterns extracted from this session's history should be saved?",
|
|
252
|
+
header: "Speichern?",
|
|
253
253
|
options: [
|
|
254
254
|
{
|
|
255
255
|
label: "[type] subject",
|
|
@@ -455,19 +455,18 @@ Use AskUserQuestion with options:
|
|
|
455
455
|
AskUserQuestion({
|
|
456
456
|
questions: [{
|
|
457
457
|
question: "What would you like to do with your learnings?",
|
|
458
|
-
header: "
|
|
458
|
+
header: "Learnings",
|
|
459
459
|
options: [
|
|
460
|
-
{ label: "
|
|
461
|
-
{ label: "
|
|
462
|
-
{ label: "Delete specific learnings", description: "
|
|
463
|
-
{ label: "
|
|
464
|
-
{ label: "Done — no changes", description: "Exit without changes" }
|
|
460
|
+
{ label: "Confidence ändern", description: "Pick the learnings, then the direction: +0.15 or -0.2. Cheapest fix when a learning is merely mis-weighted." },
|
|
461
|
+
{ label: "Ablauf verlängern", description: "Keeps a still-useful learning alive: its expiry date moves to today plus the configured window. Confidence is untouched." },
|
|
462
|
+
{ label: "Delete specific learnings", description: "Takes the selected learnings out of the store. They are archived rather than shredded, but they stop influencing anything." },
|
|
463
|
+
{ label: "Done — no changes", description: "Leaves the store exactly as it is and ends the review. Nothing is written." }
|
|
465
464
|
]
|
|
466
465
|
}]
|
|
467
466
|
})
|
|
468
467
|
```
|
|
469
468
|
|
|
470
|
-
If user selects "
|
|
469
|
+
If user selects "Confidence ändern", "Ablauf verlängern", or "Delete specific learnings", present a follow-up AskUserQuestion with `multiSelect: true` listing all learnings by `# | type | subject` so the user can select which ones to modify. For "Confidence ändern" the same follow-up also asks for the direction — **Boost** (+0.15) or **Reduce** (-0.2). Both operations are unchanged; only the point at which the direction is chosen moved, because a single AskUserQuestion accepts at most 4 options and the previous list had 5.
|
|
471
470
|
|
|
472
471
|
> On Codex CLI where AskUserQuestion is unavailable, present as a numbered Markdown list.
|
|
473
472
|
|
|
@@ -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
|
+
paths 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
|
|
@@ -37,7 +45,7 @@ syntax inline. This skill is the single source of truth for all VCS operations.
|
|
|
37
45
|
When a skill needs VCS operations, include this reference block in its instructions:
|
|
38
46
|
|
|
39
47
|
> **VCS Reference:** Detect the VCS platform per the "VCS Auto-Detection" section of the gitlab-ops skill.
|
|
40
|
-
> Use CLI commands per the "Common CLI Commands" section. For
|
|
48
|
+
> Use CLI commands per the "Common CLI Commands" section. For GitLab API operations, see "Canonical Project Identity."
|
|
41
49
|
|
|
42
50
|
**Canonical commands:** All `glab` and `gh` command syntax — flags, output formats,
|
|
43
51
|
pagination options — is defined in the "Common CLI Commands" section below. Consuming
|
|
@@ -49,34 +57,35 @@ command variant not listed there, add it to this file first, then reference it.
|
|
|
49
57
|
- Any skill-specific *parameters* they pass to commands (e.g., label names, issue templates)
|
|
50
58
|
- They should NOT include raw `glab`/`gh` invocations or detection snippets
|
|
51
59
|
|
|
52
|
-
##
|
|
60
|
+
## Canonical Project Identity
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
GitLab REST endpoints accept a URL-encoded `namespace/project` path. Select the GitLab host and project path explicitly; never derive a numeric project ID from `glab repo view`, search `projects?search=`, or use `:id` placeholders. Those forms can resolve through the ambient working directory or a stale search result and target another project after a rename, fork, or scaffold.
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
Set the identity once per operation sequence and reuse the encoded identifier without encoding it again:
|
|
57
65
|
|
|
58
66
|
```bash
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
GITLAB_HOST="<selected GitLab hostname>"
|
|
68
|
+
GROUP_PATH="<selected group path>"
|
|
69
|
+
PROJECT_NAME="<selected project name>"
|
|
70
|
+
PROJECT_PATH="$GROUP_PATH/$PROJECT_NAME"
|
|
71
|
+
ENCODED_PROJECT_PATH="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$PROJECT_PATH")"
|
|
72
|
+
```
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
For a link target in another project, use the same path-first shape instead of a numeric ID:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
TARGET_PROJECT_PATH="<target namespace>/<target project>"
|
|
78
|
+
TARGET_ENCODED_PROJECT_PATH="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$TARGET_PROJECT_PATH")"
|
|
64
79
|
```
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
Pass `--hostname "$GITLAB_HOST"` to every `glab api` call. The endpoint itself then pins the project, including directly after creating a repository when the current directory does not yet identify the new project.
|
|
67
82
|
|
|
68
|
-
|
|
83
|
+
GitHub continues to use an `owner/repo` slug; `gh repo` takes it positionally and rejects `-R`:
|
|
69
84
|
|
|
70
85
|
```bash
|
|
71
|
-
|
|
72
|
-
glab api "projects?search=<project-name>" | python3 -c "import json,sys; [print(p['id'], p['path_with_namespace']) for p in json.load(sys.stdin)]"
|
|
73
|
-
|
|
74
|
-
# GitHub — resolve repo details
|
|
75
|
-
gh api "repos/<owner>/<name>" --jq '.full_name'
|
|
86
|
+
gh repo view --json nameWithOwner -q '.nameWithOwner'
|
|
76
87
|
```
|
|
77
88
|
|
|
78
|
-
**Note:** Some API calls require numeric project IDs (GitLab) or `owner/repo` slugs (GitHub). Always resolve dynamically from the project name.
|
|
79
|
-
|
|
80
89
|
### Canonical enumeration pattern
|
|
81
90
|
|
|
82
91
|
To enumerate ALL projects (or issues) in a group, a single page is never the whole result — paginate and guard against silent truncation:
|
|
@@ -85,7 +94,7 @@ To enumerate ALL projects (or issues) in a group, a single page is never the who
|
|
|
85
94
|
# GitLab — paginate a group's projects, following x-next-page until empty
|
|
86
95
|
page=1
|
|
87
96
|
while [ -n "$page" ]; do
|
|
88
|
-
resp=$(glab api "groups/<group-id>/projects?include_subgroups=true&per_page=100&page=$page" --include)
|
|
97
|
+
resp=$(glab api --hostname "$GITLAB_HOST" "groups/<group-id>/projects?simple=true&include_subgroups=true&per_page=100&page=$page" --include)
|
|
89
98
|
# parse the response body ($resp) for project ids/paths here, deduping by id.
|
|
90
99
|
# Then advance by reading the `x-next-page` response header — an empty value
|
|
91
100
|
# means this was the last page, so the loop exits (the guard above is what breaks).
|
|
@@ -138,13 +147,16 @@ done
|
|
|
138
147
|
|
|
139
148
|
## Issue Linking (`blocks` / `is_blocked_by`)
|
|
140
149
|
|
|
141
|
-
GitLab's native issue-link types `blocks` and `is_blocked_by` (`glab api -X POST projects
|
|
150
|
+
GitLab's native issue-link types `blocks` and `is_blocked_by` (`glab api --silent --hostname "$GITLAB_HOST" -X POST "projects/${ENCODED_PROJECT_PATH}/issues/${ISSUE_IID}/links" -f target_project_id="$TARGET_ENCODED_PROJECT_PATH" -f target_issue_iid="$OTHER_ISSUE_IID" -f link_type="$LINK_TYPE"`) are a **Premium/Ultimate license feature**. Set `LINK_TYPE` to `blocks` or `is_blocked_by`; the target accepts an encoded project path, so no numeric project ID is needed. On a Free/Core-tier GitLab instance this call returns **HTTP 403** — a license-gate signal, not an auth/permission failure. Do not retry with different credentials or escalate as an auth bug.
|
|
142
151
|
|
|
143
152
|
**Fallback (non-Premium instances):**
|
|
144
153
|
1. **Use `relates_to` instead** — `link_type=relates_to` is available on every GitLab tier (no ordering semantics, just an unscoped relation). Same API shape, only the `link_type` value changes:
|
|
145
154
|
```bash
|
|
146
|
-
glab api -X POST
|
|
147
|
-
|
|
155
|
+
glab api --silent --hostname "$GITLAB_HOST" -X POST \
|
|
156
|
+
"projects/${ENCODED_PROJECT_PATH}/issues/${ISSUE_IID}/links" \
|
|
157
|
+
-f target_project_id="$TARGET_ENCODED_PROJECT_PATH" \
|
|
158
|
+
-f target_issue_iid="$OTHER_ISSUE_IID" \
|
|
159
|
+
-f link_type=relates_to
|
|
148
160
|
```
|
|
149
161
|
2. **Document the blocking semantics in the issue body** — since `relates_to` carries no ordering meaning, add an explicit ordering note to both issues, e.g. `⚠ Ordering: erst #<blocker_iid>, dann dieses Issue — blocks-Link nicht verfügbar (non-Premium)`.
|
|
150
162
|
3. **Recognize the 403 as a license signal, not an auth error** — before assuming a token/scope problem, try `relates_to` on the same project pair: if `relates_to` succeeds where `blocks`/`is_blocked_by` 403s, the license gate — not authentication — is the cause.
|
|
@@ -153,38 +165,40 @@ GitHub has no native issue-blocking relation at all — the body-ordering-note f
|
|
|
153
165
|
|
|
154
166
|
## Common CLI Commands
|
|
155
167
|
|
|
168
|
+
**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.
|
|
169
|
+
|
|
156
170
|
### GitLab (glab)
|
|
157
171
|
|
|
158
172
|
```bash
|
|
159
173
|
# 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"
|
|
174
|
+
glab issue list -R <OWNER>/<REPO> --per-page 50 # All open issues
|
|
175
|
+
glab issue list -R <OWNER>/<REPO> --label "status:ready" --per-page 10 # Ready to work on
|
|
176
|
+
glab issue list -R <OWNER>/<REPO> --label "priority::high" --per-page 10 # High priority
|
|
177
|
+
glab issue list -R <OWNER>/<REPO> --closed --per-page 10 # Recently closed
|
|
178
|
+
glab issue view -R <OWNER>/<REPO> <IID> # View issue details
|
|
179
|
+
glab issue view -R <OWNER>/<REPO> <IID> --comments # With comments
|
|
180
|
+
glab issue create -R <OWNER>/<REPO> --title "title" --label "priority::high,status:ready"
|
|
181
|
+
glab issue update -R <OWNER>/<REPO> <IID> --label "status:in-progress" # WARNING: --label REPLACES the full set — see caveat below
|
|
182
|
+
glab issue close -R <OWNER>/<REPO> <IID> # then VERIFY: re-read the issue; it must show state=closed
|
|
183
|
+
glab issue note -R <OWNER>/<REPO> <IID> -m "Comment text" # Add comment
|
|
170
184
|
|
|
171
185
|
# MRs
|
|
172
|
-
glab mr list
|
|
173
|
-
glab mr create --fill --draft
|
|
174
|
-
glab mr merge <MR_IID>
|
|
186
|
+
glab mr list -R <OWNER>/<REPO> # Open MRs
|
|
187
|
+
glab mr create -R <OWNER>/<REPO> --fill --draft # Create draft MR
|
|
188
|
+
glab mr merge -R <OWNER>/<REPO> <MR_IID> # Merge MR
|
|
175
189
|
|
|
176
190
|
# Pipelines
|
|
177
|
-
glab pipeline list --per-page 5
|
|
178
|
-
glab pipeline status <ID>
|
|
191
|
+
glab pipeline list -R <OWNER>/<REPO> --per-page 5 # Recent pipelines
|
|
192
|
+
glab pipeline status -R <OWNER>/<REPO> <ID> # Pipeline details
|
|
179
193
|
|
|
180
|
-
# API (
|
|
181
|
-
glab api
|
|
182
|
-
glab api
|
|
194
|
+
# API (no --repo exists here — the encoded endpoint and explicit host identify the target)
|
|
195
|
+
glab api --hostname "$GITLAB_HOST" "projects/${ENCODED_PROJECT_PATH}/issues?state=opened&per_page=50"
|
|
196
|
+
glab api --hostname "$GITLAB_HOST" "projects/${ENCODED_PROJECT_PATH}/milestones?state=active"
|
|
183
197
|
```
|
|
184
198
|
|
|
185
199
|
**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`.
|
|
186
200
|
|
|
187
|
-
**Close verification:** after `glab issue close <IID>`, always verify the close actually landed — re-read the issue (`glab issue view <IID>`) and confirm `state: closed` in the output. A stale
|
|
201
|
+
**Close verification:** after `glab issue close <IID>`, always verify the close actually landed — re-read the issue (`glab issue view <IID>`) and confirm `state: closed` in the output. A stale or wrong project path, or a silent 404, can report local success while closing nothing; use the canonical project identity above for API operations rather than resolving a numeric ID.
|
|
188
202
|
|
|
189
203
|
**Commit-body close-keyword footgun:** GitLab (and GitHub) auto-close an issue when a commit pushed to the default branch contains a close keyword — `close`/`closes`/`closed`/`fix`/`fixes`/`fixed`/`resolve`/`resolves`/`resolved` — followed by `#N` ANYWHERE in the commit body, not just the subject line. This fires even inside a negation ("does NOT close #N") — the platform pattern-matches the keyword + issue reference; it does not parse English negation, so the negation offers no protection. Rule: when a commit body needs to MENTION an issue without closing intent, always use a non-closing reference — `refs #N`, `part of #N`, `siehe #N` — never a close-keyword verb next to the number, negated or not.
|
|
190
204
|
|
|
@@ -194,27 +208,27 @@ glab api "projects/$(glab repo view --output json | python3 -c "import json,sys;
|
|
|
194
208
|
|
|
195
209
|
```bash
|
|
196
210
|
# 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"
|
|
211
|
+
gh issue list -R <OWNER>/<REPO> --limit 50 # All open issues
|
|
212
|
+
gh issue list -R <OWNER>/<REPO> --label "status:ready" --limit 10 # Ready to work on
|
|
213
|
+
gh issue list -R <OWNER>/<REPO> --label "priority::high" --limit 10 # High priority
|
|
214
|
+
gh issue list -R <OWNER>/<REPO> --state closed --limit 10 # Recently closed
|
|
215
|
+
gh issue view -R <OWNER>/<REPO> <NUMBER> # View issue details
|
|
216
|
+
gh issue view -R <OWNER>/<REPO> <NUMBER> --comments # With comments
|
|
217
|
+
gh issue create -R <OWNER>/<REPO> --title "title" --label "priority::high,status:ready"
|
|
218
|
+
gh issue edit -R <OWNER>/<REPO> <NUMBER> --add-label "status:in-progress"
|
|
219
|
+
gh issue close -R <OWNER>/<REPO> <NUMBER>
|
|
220
|
+
gh issue comment -R <OWNER>/<REPO> <NUMBER> --body "Comment text" # Add comment
|
|
207
221
|
|
|
208
222
|
# PRs
|
|
209
|
-
gh pr list --state open
|
|
210
|
-
gh pr create --fill --draft
|
|
211
|
-
gh pr merge <NUMBER>
|
|
223
|
+
gh pr list -R <OWNER>/<REPO> --state open # Open PRs
|
|
224
|
+
gh pr create -R <OWNER>/<REPO> --fill --draft # Create draft PR
|
|
225
|
+
gh pr merge -R <OWNER>/<REPO> <NUMBER> # Merge PR
|
|
212
226
|
|
|
213
227
|
# Workflows (CI equivalent)
|
|
214
|
-
gh run list --limit 5
|
|
215
|
-
gh run view <RUN_ID>
|
|
228
|
+
gh run list -R <OWNER>/<REPO> --limit 5 # Recent workflow runs
|
|
229
|
+
gh run view -R <OWNER>/<REPO> <RUN_ID> # Run details
|
|
216
230
|
|
|
217
|
-
# API
|
|
231
|
+
# API (no --repo exists here — the endpoint path IS the target; pin the host with --hostname)
|
|
218
232
|
gh api "repos/{owner}/{repo}/issues?state=open&per_page=50"
|
|
219
233
|
gh api "repos/{owner}/{repo}/milestones?state=open"
|
|
220
234
|
```
|
|
@@ -367,8 +381,8 @@ Read .gitlab/merge_request_templates/Default.md
|
|
|
367
381
|
Read .github/PULL_REQUEST_TEMPLATE.md
|
|
368
382
|
|
|
369
383
|
# 2. Then create — hook now passes
|
|
370
|
-
glab mr create --title "..." --description "..."
|
|
371
|
-
gh pr create --title "..." --body "..."
|
|
384
|
+
glab mr create -R <OWNER>/<REPO> --title "..." --description "..."
|
|
385
|
+
gh pr create -R <OWNER>/<REPO> --title "..." --body "..."
|
|
372
386
|
```
|
|
373
387
|
|
|
374
388
|
### 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/SKILL.md
CHANGED
|
@@ -66,7 +66,7 @@ Walk the decision tree **one question at a time**. For each branch, in order:
|
|
|
66
66
|
AskUserQuestion({
|
|
67
67
|
questions: [{
|
|
68
68
|
question: "Your code cancels whole Orders, but the PRD says a customer can cancel one line item. Which is the real model?",
|
|
69
|
-
header: "
|
|
69
|
+
header: "Cancel scope",
|
|
70
70
|
options: [
|
|
71
71
|
{ label: "Line-item cancellation (Recommended)", description: "Matches the PRD intent. Cost: new partial-refund path + Order stays open after one item is voided." },
|
|
72
72
|
{ label: "Whole-order only", description: "Matches today's code. Cost: contradicts the stated user story — re-scope the PRD." },
|
|
@@ -118,12 +118,12 @@ Per the user's configured behaviour, the grill ends with a hand-off and an OPTIO
|
|
|
118
118
|
AskUserQuestion({
|
|
119
119
|
questions: [{
|
|
120
120
|
question: "Grill complete. How do you want to proceed?",
|
|
121
|
-
header: "
|
|
121
|
+
header: "Hand-off",
|
|
122
122
|
options: [
|
|
123
|
-
{ label: "
|
|
124
|
-
{ label: "Write grill summary only", description: "
|
|
125
|
-
{ label: "Hand off to /plan feature — no file", description: "
|
|
126
|
-
{ label: "Done — no file, no hand-off", description: "The grilling itself was the value
|
|
123
|
+
{ label: "Summary + /plan feature (Recommended)", description: "Writes the resolved decisions to docs/specs/ (a folder of markdown notes), then hands them to planning. Cost: one file." },
|
|
124
|
+
{ label: "Write grill summary only", description: "Same file, no planning step. The decisions keep, so you can hand them off later." },
|
|
125
|
+
{ label: "Hand off to /plan feature — no file", description: "Carries the decisions straight into planning. They then live only in this conversation." },
|
|
126
|
+
{ label: "Done — no file, no hand-off", description: "The grilling itself was the value. Nothing is written, nothing is handed on." }
|
|
127
127
|
],
|
|
128
128
|
multiSelect: false
|
|
129
129
|
}]
|
package/skills/grill/soul.md
CHANGED
|
@@ -76,6 +76,22 @@ This is the tightest of the orchestrator's budgets by design. A turn is structur
|
|
|
76
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
77
|
- Escalation: `expand <topic>` — see § Escalation above.
|
|
78
78
|
|
|
79
|
+
### Register — how a sentence reads
|
|
80
|
+
|
|
81
|
+
The budgets above set *how much* you say; the register sets *how*. It is
|
|
82
|
+
defined once, in `skills/session-start/soul.md` § "Register — how a sentence
|
|
83
|
+
reads", and binds here unchanged: the frame ("write for someone who knows this
|
|
84
|
+
project but has not seen what you just saw"), the plain-words test with its
|
|
85
|
+
five worked cases, and its precedence over § "Never traded for brevity" above.
|
|
86
|
+
Read it there. It is not repeated here on purpose — the § Output Levels intro
|
|
87
|
+
sentence already exists in four copies across the four souls with nothing
|
|
88
|
+
checking their parity, and a fifth copied rule would drift the same way. A
|
|
89
|
+
pointer cannot.
|
|
90
|
+
|
|
91
|
+
Note the one place register and adversarial posture meet: a challenge is
|
|
92
|
+
plainer, never softer. "Say it more simply" never becomes "say it less
|
|
93
|
+
sharply" — the contradiction still lands, in shorter words.
|
|
94
|
+
|
|
79
95
|
### Companion dials
|
|
80
96
|
|
|
81
97
|
Same file, same lookup, same fallback-to-default rule:
|
|
@@ -246,8 +246,8 @@ const peerWarning = wt.activePeer
|
|
|
246
246
|
|
|
247
247
|
AskUserQuestion({
|
|
248
248
|
questions: [{
|
|
249
|
-
question: `
|
|
250
|
-
header: "
|
|
249
|
+
question: `Auto-promoted worktree ${path.basename(wt.wtPath)} is ${ageDays}d old (branch ${wt.branch}).${peerWarning} Remove?`,
|
|
250
|
+
header: "Worktree",
|
|
251
251
|
multiSelect: false,
|
|
252
252
|
options: [
|
|
253
253
|
{
|
|
@@ -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
|
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
name: npm-publish
|
|
3
3
|
user-invocable: true
|
|
4
4
|
model: sonnet
|
|
5
|
-
description: Use when publishing this package to npm — a version release (npm publish), verifying the registry/pi.dev listing, or diagnosing npm auth failures (E403 2FA/token errors). Token-based flow via NPM_TOKEN in .env.local with a temp userconfig, leakage
|
|
5
|
+
description: Use when publishing this package to npm — a version release (npm publish), verifying the registry/pi.dev listing, or diagnosing npm auth failures (E403 2FA/token errors). Token-based flow via NPM_TOKEN in .env.local with a temp userconfig, the leakage gate before every publish, post-publish verification and marker/badge upkeep. Trigger on "publish to npm", "npm release", "E403 publish error".
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# npm-publish —
|
|
8
|
+
# npm-publish — token auth, and the calls the script cannot make
|
|
9
9
|
|
|
10
|
-
>
|
|
10
|
+
> **The release itself is `/release` → `scripts/release.mjs`.** That script mechanizes the whole sequence: version surfaces, CHANGELOG gate, drift sweep, tag/registry collision, CI, leakage gate, publish, the target-confirmed npm receipt boundary, tag-after-receipt, push to both remotes, GitHub-release handling, and live-site poll. This skill does not restate it.
|
|
11
|
+
>
|
|
12
|
+
> What lives here is the half a script cannot own: the **token setup**, the **auth failure diagnosis**, and the **judgement calls** — which version, what a leak means, when to abort rather than repair.
|
|
11
13
|
|
|
12
14
|
## Why this skill exists
|
|
13
15
|
|
|
@@ -24,60 +26,20 @@ Create at https://www.npmjs.com/settings/<user>/tokens → Generate New Token
|
|
|
24
26
|
|
|
25
27
|
## Auth resolution order
|
|
26
28
|
|
|
27
|
-
1. `NPM_TOKEN` in `.env.local` at the repo root (gitignored — verify with `git check-ignore .env.local` before writing; also confirm no `.env` pattern in the `files` whitelist of package.json).
|
|
29
|
+
1. `NPM_TOKEN` in `.env.local` at the repo root (gitignored — verify with `git check-ignore .env.local` before writing; also confirm no `.env` pattern in the `files` whitelist of package.json). `scripts/release.mjs` refuses to read the token if that ignore check fails.
|
|
28
30
|
2. Interactive fallback: operator runs `npm publish --access public` in a real terminal (only works when account 2FA is enrolled — OTP prompt appears).
|
|
29
31
|
|
|
30
32
|
**Never** put the token in the tracked `.npmrc` (it holds `ignore-scripts=true` per SEC-020 and is committed), never persist it into `~/.npmrc`, never echo it into logs.
|
|
31
33
|
|
|
32
|
-
##
|
|
34
|
+
## The three judgement calls
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
version-surface sync this skill previously left to operator memory (the gap that let v3.18.0
|
|
36
|
-
ship tagged but unpublished):
|
|
36
|
+
The script gates mechanics. These three are yours, and it will not make them for you.
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
node scripts/release.mjs --set-version X.Y.Z # rewrite all 12 version literals (10 files) + codex cachebuster + lock sync
|
|
40
|
-
# … author CHANGELOG entry + README highlights (enforced by --check) …
|
|
41
|
-
node scripts/release.mjs --check # preflight: surfaces, CHANGELOG, tag/registry collision, CI green, leakage gate
|
|
42
|
-
node scripts/release.mjs --publish # token publish → registry verify → tag AFTER publish → push origin+github
|
|
43
|
-
```
|
|
38
|
+
**1. Which version is the right one.** Semver per `.claude/rules/development.md` § Package Lifecycle & Versioning: patch = fixes/docs/internal refactor; minor = additive and backwards-compatible; major = removed or renamed exports, or changed runtime behaviour — and a major never merges without a migration guide and a `BREAKING CHANGE:` footer. The script validates the *shape* `X.Y.Z` and nothing about whether the number matches the diff. Read the CHANGELOG entry you just wrote and ask whether a consumer pinning `^` would be broken by it; if yes, the bump is a major regardless of how small the diff looks.
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
below remains as the fallback and as documentation of what the script does.
|
|
40
|
+
**2. What a leak means when one is found.** A hit from the leakage gate is not a pattern to silence. Decide which of two it is: a real leak (fix `package.json` `files`, re-pack, re-check) or genuine over-matching (fix `LEAKAGE_PATTERNS` in `scripts/release.mjs` **with a test**). There is no third option, and neither is "publish anyway and clean it up in the next version" — an npm publish is not revocable, and unpublishing burns the version number permanently. Operator handling detail: `docs/distribution/npm-publish-checklist.md` § 3.
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
# 1. Pre-flight (first publish: expect E404 = name free; upgrade: expect the previous version)
|
|
52
|
-
npm view session-orchestrator version
|
|
53
|
-
|
|
54
|
-
# 2. Leakage gate — every grep MUST print 0 (from docs/distribution/npm-publish-checklist.md)
|
|
55
|
-
npm pack --dry-run 2>&1 | grep -cE "npm notice.* tests/"
|
|
56
|
-
npm pack --dry-run 2>&1 | grep -c "npm notice.*\.orchestrator/"
|
|
57
|
-
npm pack --dry-run 2>&1 | grep -cE "npm notice.*[[:space:]]\.claude/"
|
|
58
|
-
npm pack --dry-run 2>&1 | grep -c "npm notice.*\.github/"
|
|
59
|
-
npm pack --dry-run 2>&1 | grep -c "node_modules"
|
|
60
|
-
npm pack --dry-run 2>&1 | grep -ci "\.env"
|
|
61
|
-
npm pack --dry-run 2>&1 | grep -ci "owner\.yaml"
|
|
62
|
-
|
|
63
|
-
# 3. Publish via temp userconfig (never a persistent npmrc)
|
|
64
|
-
NPM_TOKEN=$(grep '^NPM_TOKEN=' .env.local | cut -d= -f2-)
|
|
65
|
-
TMPRC=$(mktemp) && printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_TOKEN" > "$TMPRC" && chmod 600 "$TMPRC"
|
|
66
|
-
npm publish --access public --userconfig "$TMPRC"; rm "$TMPRC"
|
|
67
|
-
|
|
68
|
-
# 4. Verify
|
|
69
|
-
npm view session-orchestrator version # must print the new version
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Success marker: `+ session-orchestrator@<version>` on the publish output.
|
|
73
|
-
|
|
74
|
-
## Post-publish checklist
|
|
75
|
-
|
|
76
|
-
1. **Verify registry**: `npm view session-orchestrator version dist.unpackedSize keywords` — `pi-package` keyword must be present.
|
|
77
|
-
2. **pi.dev gallery**: indexing is asynchronous — check https://pi.dev/packages later; do not block on it.
|
|
78
|
-
3. **Marker upkeep** (first publish only — done in v3.16.0): README install matrix + npm badge, `site/index.html` install section, `docs/pi-setup.md` availability paragraph.
|
|
79
|
-
4. **Rotate/delete the token** at https://www.npmjs.com/settings/<user>/tokens once the release is done — especially if the token value ever transited chat, a screenshot, or any log. A token pasted into a conversation is burned: rotate immediately after use.
|
|
80
|
-
5. Update the release issue / CHANGELOG if the publish was part of a tracked release.
|
|
42
|
+
**3. When to abort instead of repair.** Abort — do not patch forward — when the failure is upstream of the target-confirmed npm receipt: a red preflight row, a lagging `github` mirror, CI not green on the exact commit, a dead token, or a publish that did not issue the target receipt. These are cheap to fix and re-run from the top. Repair-in-place is only appropriate *after* that receipt, where the version is already immutable: registry propagation, a missing GitHub release, or a lagging site deploy can be reconciled because npm already has the correct artifact. When `--publish` reports **Post-publish reconciliation required**, **do not rerun `--publish`**; repair the listed state directly. `commands/release.md` § Abort criteria is the operative list.
|
|
81
43
|
|
|
82
44
|
## Failure-mode table
|
|
83
45
|
|
|
@@ -85,13 +47,23 @@ Success marker: `+ session-orchestrator@<version>` on the publish output.
|
|
|
85
47
|
|---|---|---|
|
|
86
48
|
| `E403 ... Two-factor authentication or granular access token with bypass 2fa enabled is required` — no OTP prompt | Account has no 2FA enrolled AND token (if any) lacks Bypass-2FA | Create granular token with all four requirements above, or enroll 2FA |
|
|
87
49
|
| Same E403 despite a fresh token | Token created without the Bypass-2FA checkbox, or Read-only, or package-scoped on a first publish | Re-create: RW + All packages + Bypass-2FA |
|
|
88
|
-
| `
|
|
50
|
+
| `npm whoami` silent or non-zero | Token expired, or `.env.local` missing | Re-create the token; do not proceed — the preflight fails this row on purpose |
|
|
51
|
+
| `E404` on `npm view` after a target-confirmed publish receipt | Registry propagation (rare, seconds) | Let the script finish its tag/push/GitHub/site tail, then reconcile the registry result; do **not** rerun `--publish` |
|
|
89
52
|
| `ENEEDAUTH` | No login/token at all | Token flow above, or `npm login` |
|
|
90
53
|
| OTP prompt appears but flow is non-interactive (`!`-prefix, script) | No TTY for the prompt | Use the token flow, or a real terminal |
|
|
91
54
|
|
|
55
|
+
## Post-publish — the human half
|
|
56
|
+
|
|
57
|
+
`--publish` attempts registry verification and polls the live site itself. A target-confirmed receipt plus a delayed registry result is a reconciliation outcome, not a failed publish or a retry instruction. What still needs a person:
|
|
58
|
+
|
|
59
|
+
1. **Rotate/delete the token** at https://www.npmjs.com/settings/<user>/tokens. A token that ever transited a conversation, a screenshot, or any log is burned — rotate immediately.
|
|
60
|
+
2. **pi.dev gallery**: indexing is asynchronous — check https://pi.dev/packages later; do not block on it.
|
|
61
|
+
3. **Marker upkeep** on a first publish only (done in v3.16.0): README install matrix + npm badge, `site/index.html` install section, `docs/pi-setup.md` availability paragraph.
|
|
62
|
+
4. Update the release issue if the publish was part of tracked work.
|
|
63
|
+
|
|
92
64
|
## Security invariants
|
|
93
65
|
|
|
94
66
|
- `.env.local` is gitignored AND absent from the npm `files` whitelist — verify both before writing a token into it.
|
|
95
|
-
- Temp userconfig: `chmod 600`, deleted immediately after publish.
|
|
67
|
+
- Temp userconfig: `chmod 600`, deleted in a `finally` block immediately after publish.
|
|
96
68
|
- The leakage gate runs before EVERY publish, not only the first.
|
|
97
69
|
- npm's own recommendation for unattended CI/CD is **Trusted Publishing** (OIDC) — evaluate it if publishing ever moves into CI (ref: https://docs.npmjs.com/about-access-tokens).
|
|
@@ -96,10 +96,10 @@ For each `${PERM_NAME}` in `$MISSING`:
|
|
|
96
96
|
```
|
|
97
97
|
AskUserQuestion({
|
|
98
98
|
questions: [{
|
|
99
|
-
question: `${PERM_NAME}
|
|
100
|
-
header:
|
|
99
|
+
question: `${PERM_NAME} is not granted. Enable the terminal entry under System Settings > Privacy & Security, then confirm.`,
|
|
100
|
+
header: "Zugriff",
|
|
101
101
|
options: [
|
|
102
|
-
{ label: "Granted — continue (Recommended)", description:
|
|
102
|
+
{ label: "Granted — continue (Recommended)", description: "I enabled the terminal entry in that pane — the driver then checks again and carries on if the grant took effect." },
|
|
103
103
|
{ label: "Skip this run", description: "Abort peekaboo-driver. Test-runner will record a framework-error finding." }
|
|
104
104
|
],
|
|
105
105
|
multiSelect: false
|