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
|
@@ -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, tag-after-publish, push to both remotes, 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 publish: a red preflight row, a lagging `github` mirror, CI not green on the exact commit, a dead token. These are cheap to fix and re-run from the top. Repair-in-place is only ever appropriate *after* a verified publish, where the version is already immutable: a missing GitHub release or a lagging site deploy can be reconciled, because npm already has the correct artifact. The dividing line is whether the registry has accepted the tarball — before that point, restart; after it, reconcile. `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 |
|
|
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 |
|
|
88
51
|
| `E404` on `npm view` after publish | Registry propagation (rare, seconds) or publish actually failed | Re-check the publish output for `+ <name>@<version>` |
|
|
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` verifies the registry and polls the live site itself, and prints the rest. 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).
|
|
@@ -40,7 +40,9 @@ catalog rather than referencing the plugin copy in place.
|
|
|
40
40
|
Install:
|
|
41
41
|
```bash
|
|
42
42
|
mkdir -p .claude/personas
|
|
43
|
-
|
|
43
|
+
# Claude Code has no `plugin dir` subcommand — resolve the install path from the cache.
|
|
44
|
+
SO_DIR="$(dirname "$(find ~/.claude/plugins/cache -path '*session-orchestrator*' -name package.json 2>/dev/null | head -1)")"
|
|
45
|
+
cp "$SO_DIR/skills/persona-panel/presets/"*.md .claude/personas/
|
|
44
46
|
```
|
|
45
47
|
|
|
46
48
|
## Phase 0: Bootstrap Gate
|
package/skills/plan/mode-new.md
CHANGED
|
@@ -38,7 +38,7 @@ Agent({ subagent_type: "Explore", description: "Check ecosystem for conflicts",
|
|
|
38
38
|
3. **Target audience** — Options informed by market research agent. User selects or provides custom.
|
|
39
39
|
4. **User-Story-Schicht** — "User-Story-Schicht für dieses Feature erzeugen?" Immer fragen (kein Audience-Heuristik-Gate). Drei Antwortoptionen: **Ja (Als/möchte/damit)** — klassische Persona-Story-Form; **Ja (job-story)** — job-story-Form ("When [situation], I want [motivation], so I can [outcome]"); **Nein** — byte-identisches Status-quo-Verhalten. Bei einer der beiden "Ja"-Optionen emittiert die PRD eine optionale ## User Stories Sektion (je Story ein ↳ AC-Pointer) in der gewählten Form; bei "Nein" wird die Sektion vollständig weggelassen.
|
|
40
40
|
5. **Core problem being solved** — Open-ended. Claude suggests structure if answer is vague.
|
|
41
|
-
6. **GitLab group** — Discover available groups dynamically. Run `ls $BASELINE_PATH/templates/` for project types, and check for a groups config in `$BASELINE_PATH/config/` or
|
|
41
|
+
6. **GitLab group** — Discover available groups dynamically. Run `ls $BASELINE_PATH/templates/` for project types, and check for a groups config in `$BASELINE_PATH/config/` or run `glab api "groups?per_page=100&min_access_level=10"` to discover GitLab groups — read each entry's `full_path` field. (`glab` has no `group` subcommand at all — invoking one exits 1 with `Unknown command "group"`.) Present findings via AskUserQuestion.
|
|
42
42
|
|
|
43
43
|
### Wave 2 — Technical Details (5 questions, dynamic per archetype)
|
|
44
44
|
|
|
@@ -129,6 +129,11 @@ Map gathered answers to script input choices:
|
|
|
129
129
|
# 4. Map user's selected style name → numeric choice for STYLE_CHOICE (if applicable)
|
|
130
130
|
# 5. Map user's selected group name → numeric choice for GROUP_CHOICE
|
|
131
131
|
# Do NOT hardcode numeric mappings — they must be derived from the script.
|
|
132
|
+
#
|
|
133
|
+
# GROUP_CHOICE is a MENU INDEX, never a namespace. Keep the group's real
|
|
134
|
+
# namespace in a separate variable — every later step addresses the project as
|
|
135
|
+
# "<group-path>/<project>", and a numeric index there silently targets nothing.
|
|
136
|
+
GROUP_PATH="$(...)" # e.g., "products" — the full_path of the chosen group
|
|
132
137
|
(
|
|
133
138
|
echo "$TYPE_CHOICE" # e.g., "1" for nextjs-saas
|
|
134
139
|
echo "$STYLE_CHOICE" # e.g., "1" for vega (only if nextjs-saas)
|
|
@@ -143,7 +148,7 @@ Map gathered answers to script input choices:
|
|
|
143
148
|
Check exit code. Confirm repo exists:
|
|
144
149
|
|
|
145
150
|
```bash
|
|
146
|
-
glab repo view $
|
|
151
|
+
glab repo view "$GROUP_PATH/$PROJECT_NAME"
|
|
147
152
|
```
|
|
148
153
|
|
|
149
154
|
### Step 3: Adjust visibility
|
|
@@ -151,9 +156,22 @@ glab repo view $GROUP/$PROJECT_NAME
|
|
|
151
156
|
If visibility is not `internal` (the default):
|
|
152
157
|
|
|
153
158
|
```bash
|
|
154
|
-
glab repo edit
|
|
159
|
+
# There is no `glab repo edit`, and `glab repo update` carries no --visibility
|
|
160
|
+
# flag (its FLAGS are --archive/--defaultBranch/-d/--description). Go through the
|
|
161
|
+
# API, addressing the project by its URL-encoded path — `projects/:id` resolves
|
|
162
|
+
# from the CWD remote, which is the wrong project right after scaffolding.
|
|
163
|
+
ENCODED="${GROUP_PATH}%2F${PROJECT_NAME}"
|
|
164
|
+
glab api -X PUT "projects/${ENCODED}" -f visibility=private # or visibility=public
|
|
165
|
+
|
|
166
|
+
# Verify (this GET is the read-only proof the PUT landed). Note `glab api` has
|
|
167
|
+
# no --jq flag — that is `gh api`'s. Pipe to jq instead.
|
|
168
|
+
glab api "projects/${ENCODED}" | jq -r '.visibility'
|
|
155
169
|
```
|
|
156
170
|
|
|
171
|
+
> The GET path above is verified against glab 1.91.0; the PUT is the documented
|
|
172
|
+
> GitLab API shape but was **not** executed during authoring (write operations
|
|
173
|
+
> were out of scope). Confirm with the GET before relying on it.
|
|
174
|
+
|
|
157
175
|
For public/OSS, also configure GitHub mirror if applicable.
|
|
158
176
|
|
|
159
177
|
### Step 4: Set branch protection
|
|
@@ -274,11 +292,11 @@ Use AskUserQuestion to present the full issue structure:
|
|
|
274
292
|
|
|
275
293
|
```bash
|
|
276
294
|
# Create epic
|
|
277
|
-
glab issue create --title "$EPIC_TITLE" --description "$EPIC_DESC" \
|
|
295
|
+
glab issue create -R "$GROUP_PATH/$PROJECT_NAME" --title "$EPIC_TITLE" --description "$EPIC_DESC" \
|
|
278
296
|
--label "type:epic,priority::$PRIORITY" --milestone "$MILESTONE"
|
|
279
297
|
|
|
280
298
|
# Create sub-issues
|
|
281
|
-
glab issue create --title "$ISSUE_TITLE" --description "$ISSUE_DESC" \
|
|
299
|
+
glab issue create -R "$GROUP_PATH/$PROJECT_NAME" --title "$ISSUE_TITLE" --description "$ISSUE_DESC" \
|
|
282
300
|
--label "type:feature,priority::$PRIORITY,status:ready,area:$AREA,appetite:$APPETITE"
|
|
283
301
|
```
|
|
284
302
|
|
package/skills/plan/soul.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
You are the Plan Skill — a Product Strategist who has shipped dozens of products and knows the difference between a good idea and a shippable product. You don't collect features; you drive planning outcomes. You think in user value, not technology. You care about what problem gets solved, not which framework gets used.
|
|
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 speak technical when they're technical. You meet people where they are.
|
|
8
8
|
|
|
9
9
|
## Communication Principles
|
|
10
10
|
|
|
@@ -21,11 +21,11 @@ You respond in {{owner.language}} when that matches the user's language. You spe
|
|
|
21
21
|
- Vague questions get vague answers. Your questions are specific because you did the homework.
|
|
22
22
|
|
|
23
23
|
### Speak the user's language
|
|
24
|
-
-
|
|
24
|
+
- Language follows the operator, not the topic — the lookup is in § Identity.
|
|
25
25
|
- Match the abstraction level: business stakeholders get outcomes, engineers get implementation details.
|
|
26
26
|
- Avoid jargon when clarity works better. Use jargon when precision demands it.
|
|
27
27
|
- Short sentences. No filler. Every question earns its interruption.
|
|
28
|
-
-
|
|
28
|
+
- How much you say is a dial the operator sets, not a matter of taste — see § Output Levels.
|
|
29
29
|
|
|
30
30
|
### Drive convergence
|
|
31
31
|
- Each wave narrows the solution space. Start broad, end specific.
|
|
@@ -33,6 +33,49 @@ You respond in {{owner.language}} when that matches the user's language. You spe
|
|
|
33
33
|
- If alignment is reached early, stop early. Three waves is the max, not the target.
|
|
34
34
|
- Progress means fewer open questions, not more.
|
|
35
35
|
|
|
36
|
+
## Output Levels
|
|
37
|
+
|
|
38
|
+
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 planning run.
|
|
39
|
+
|
|
40
|
+
**How to read a budget.** A *wave briefing* is every chat line you author between one answered question and your next `AskUserQuestion` call — the research summary, the options analysis, the recommendation. Raw Explore-agent 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 researched finding disappears, it just waits to be asked for.
|
|
41
|
+
|
|
42
|
+
**Artifacts carry no budget.** The PRD, the retro, and the issue bodies you write to disk are the deliverable, not the conversation. Budgets bound chat only; the document keeps its full evidence, and pointing at it is the preferred way to stay under one.
|
|
43
|
+
|
|
44
|
+
**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.
|
|
45
|
+
|
|
46
|
+
**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:
|
|
47
|
+
- input validation, and the reporting of invalid input;
|
|
48
|
+
- error handling, error messages, and failure disclosure — a swallowed error is never "concise";
|
|
49
|
+
- security findings, warnings, and destructive-action confirmations (PSA-003);
|
|
50
|
+
- 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;
|
|
51
|
+
- anything the operator explicitly asked to see;
|
|
52
|
+
- the reason, cost, and consequence inside an option description (`.claude/rules/ask-via-tool.md` AUQ-002/AUQ-003) — a budget never buys an option the operator cannot judge from the chat.
|
|
53
|
+
|
|
54
|
+
### output-level: ultra
|
|
55
|
+
- Meaning: telegraphic — findings, options, recommendation. No narration.
|
|
56
|
+
- Budget: ≤25 lines per wave briefing; ≤2 lines per option description; ≤1 line of preamble per tool call.
|
|
57
|
+
- Shape: bullets and tables, no prose paragraphs. Each finding as `<source> — <what it means for scope>`. Never restate what an Explore agent just printed.
|
|
58
|
+
- Escalation: `expand <topic>` — see § Escalation above.
|
|
59
|
+
|
|
60
|
+
### output-level: full
|
|
61
|
+
- Meaning: terse but complete — narration trimmed, evidence preserved. This is the default.
|
|
62
|
+
- Budget: ≤60 lines per wave briefing; ≤4 lines per option description; ≤2 lines of preamble per tool call.
|
|
63
|
+
- Shape: one line of rationale per recommendation, then the evidence. Every "I recommend" keeps its "because I found" — the pairing IS the evidence; what gets trimmed is the explanation of it.
|
|
64
|
+
- Escalation: `expand <topic>` — see § Escalation above.
|
|
65
|
+
|
|
66
|
+
### output-level: lite
|
|
67
|
+
- Meaning: verbose — the shaping reasoning is spelled out. Chosen for learning, not for speed.
|
|
68
|
+
- Budget: ≤150 lines per wave briefing; ≤10 lines per option description. Still a ceiling — `lite` is not "unbounded".
|
|
69
|
+
- Shape: name the alternatives you rejected and why, spell out the appetite and the scope cuts, define unfamiliar terms on first use.
|
|
70
|
+
- Escalation: `expand <topic>` — see § Escalation above.
|
|
71
|
+
|
|
72
|
+
### Companion dials
|
|
73
|
+
|
|
74
|
+
Same file, same lookup, same fallback-to-default rule:
|
|
75
|
+
|
|
76
|
+
- `efficiency.preamble` — `minimal` (default): at most one clause before a tool call, and only when the next step is non-obvious; never "Let me research X." immediately followed by researching X. `verbose`: one sentence before each dispatch naming what you expect the Explore agent to find.
|
|
77
|
+
- `tone.style` — `direct` (this soul's baseline: lead with the recommendation, say "that's out of scope" plainly), `neutral` (state findings without advocacy; still recommend when asked), `friendly` (same content, softer framing; never softer facts).
|
|
78
|
+
|
|
36
79
|
## Decision-Making Philosophy
|
|
37
80
|
|
|
38
81
|
When planning ambiguity arises, resolve it using this hierarchy:
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: repo-audit
|
|
3
|
-
description:
|
|
3
|
+
description: >
|
|
4
|
+
Use this skill when the user wants to audit a repository for baseline compliance, check code quality,
|
|
5
|
+
security posture, CI/CD setup, testing, documentation, and ecosystem configuration. Runs 9 checklist
|
|
6
|
+
categories and emits a Markdown report plus JSON sidecar at
|
|
7
|
+
.orchestrator/metrics/repo-audit-<timestamp>.json. <example>Context: User is in a project repo and wants
|
|
8
|
+
a baseline compliance check. user: "/repo-audit" assistant: "Running repo-audit across 9 categories —
|
|
9
|
+
Configuration, Code Quality, Git Hygiene, CI/CD, Testing, Security, Documentation, Clank Integration
|
|
10
|
+
(optional), and MCP Configuration. Will produce a Markdown checklist report and JSON sidecar."
|
|
11
|
+
<commentary>The user wants a compliance check; this skill is appropriate because it runs all 9
|
|
12
|
+
categories with pass/fail/warn/skipped statuses and writes structured output.</commentary></example>
|
|
4
13
|
model: inherit
|
|
5
14
|
color: cyan
|
|
6
15
|
---
|
|
@@ -74,7 +74,7 @@ Read back the session plan that was agreed at the start. For EACH planned item:
|
|
|
74
74
|
- Document what was completed and what remains
|
|
75
75
|
- **Do NOT file the carryover issue here (#769).** Collect a carryover **candidate** instead — append it to the in-memory candidate list that the Phase 1.65 Handover Alignment Gate consumes. The issue is filed (only if the gate confirms it) in Phase 5 Step 3. Candidate record (JS keys as `routeCandidates` / `normalizeCandidate` read them — `source-phase`→`sourcePhase`, `origin-issue`→`originIssue`; see `plan-verification.md § Candidate Record Format`):
|
|
76
76
|
- `{ task: '<original task description>', sourcePhase: '1.2', originIssue: <IID or null>, priority: '<original>', bucket: 'partially-done' }`
|
|
77
|
-
- The eventual issue keeps the source-specific `[Carryover]` template — Title `[Carryover] <original task description>`, Labels `priority::<original>` + `status:ready`, Description = what's done / what's left / context for next session.
|
|
77
|
+
- The eventual issue keeps the source-specific `[Carryover]` template — Title `[Carryover] <original task description>`, Labels `priority::<original>` + `status:ready`, Description = what's done / what's left / context for next session / **Revisit-Trigger** (mandatory — a concrete reopen condition; a deferral with no named trigger is not a deferral; see `skills/gitlab-ops/SKILL.md § Carryover Template`).
|
|
78
78
|
- Link to the original issue when applicable (record its IID as `originIssue`; a candidate with no origin issue auto-carries per the gate's routing, so nothing planned is silently forgotten).
|
|
79
79
|
|
|
80
80
|
### 1.3 Not Started Items
|
|
@@ -82,29 +82,6 @@ Read back the session plan that was agreed at the start. For EACH planned item:
|
|
|
82
82
|
- If no longer relevant: close the original issue with a comment explaining why. This is a **pre-gate disposition** — it files nothing and adds no candidate.
|
|
83
83
|
- If still relevant: **do NOT touch the original issue here.** Append a carryover candidate so the Phase 1.65 gate surfaces it — `{ task: '<item>', sourcePhase: '1.3', originIssue: <original IID>, priority: '<original>', bucket: 'not-started' }`. Phase 1.3 files no NEW `[Carryover]` issue; the candidate's disposition IS the keep-vs-carry decision on the ORIGINAL issue. If the gate carries it → ensure the original remains `status:ready`; a dropped middle-band 1.3 candidate leaves the original issue unchanged and open (no auto-close in v1).
|
|
84
84
|
|
|
85
|
-
### 1.3a Optional /goal Backlog-Drain (opt-in — #636)
|
|
86
|
-
|
|
87
|
-
> Advisory-only continuation anchor at the session-end backlog seam. Never auto-invokes `/goal`, never blocks the close. `/goal` is a user slash-command; the operator decides whether to drain now or carry over.
|
|
88
|
-
|
|
89
|
-
**Gate conditions** — ALL must be true for this nudge to surface:
|
|
90
|
-
|
|
91
|
-
1. `goal-integration.enabled: true` in Session Config (default: `false`).
|
|
92
|
-
2. `session-end-backlog` is listed in `goal-integration.seams`.
|
|
93
|
-
|
|
94
|
-
When any gate condition is false, skip this step silently — no surfaced suggestion, no STATE.md write, no AUQ.
|
|
95
|
-
|
|
96
|
-
**What it does** — when the gate fires AND ≥1 still-relevant Not-Started (§1.3) or Partially-Done (§1.2) item exists AND the operator would rather drain the backlog now than carry it to a future session, surface ONE suggested `/goal` command as an advisory bullet. Example:
|
|
97
|
-
|
|
98
|
-
```
|
|
99
|
-
/goal Drain the remaining backlog items <list>; done when each item's acceptance check passes as shown by 'npm test' output in this turn AND 'npm run typecheck' prints 0 errors in this turn, or stop after 20 turns.
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
**Advisory-only contract:** this step never auto-invokes `/goal`, never blocks the close, raises no AskUserQuestion, and writes nothing to STATE.md. It is informational prose only — the operator copies the command if they want it. The deterministic **Phase 2 Quality Gate** of session-end remains the completion authority: `/goal` keeps the loop alive across turns, but `npm test` / `npm run typecheck` / `npm run lint` and their exit codes decide whether the drained work is correct.
|
|
103
|
-
|
|
104
|
-
The `/goal` evaluator reads the transcript only and runs NO tools — it anchors CONTINUATION, never JUDGMENT. The suggested condition therefore references freshly-run gate output "in this turn's output" and embeds a bound ("or stop after N turns"). Cross-reference `.claude/rules/loop-and-monitor.md § LM-008` for the full `/goal` continuation-vs-judgment contract rather than restating it here.
|
|
105
|
-
|
|
106
|
-
**One goal per session:** only ONE `/goal` can be active at a time. This backlog seam and the inter-wave fix-loop seam (`wave-loop.md` § /goal Continuation Anchor) cannot both hold an active goal simultaneously — the operator picks one.
|
|
107
|
-
|
|
108
85
|
### 1.4 Emergent Work
|
|
109
86
|
- Tasks that were NOT in the plan but were done (fixes, discoveries)
|
|
110
87
|
- **Completed emergent work** (finished, or already dispositioned into an issue): document and attribute to the relevant issues exactly as today — this path is **NOT gated**. If a completed emergent fix warrants a follow-up/doc issue, create it immediately (unchanged behavior).
|
|
@@ -803,11 +780,53 @@ git push origin HEAD
|
|
|
803
780
|
```
|
|
804
781
|
|
|
805
782
|
### 4.4 GitHub Mirror (if configured in Session Config)
|
|
783
|
+
|
|
784
|
+
Three states, three DISTINGUISHABLE outcomes. The predecessor of this block
|
|
785
|
+
(`git remote get-url github 2>/dev/null && git push github HEAD 2>/dev/null || echo "GitHub mirror: not configured"`)
|
|
786
|
+
collapsed a **failed push** into `GitHub mirror: not configured` and exited 0 — git's real
|
|
787
|
+
error went to `/dev/null`, so a broken mirror was indistinguishable from an unconfigured one
|
|
788
|
+
(`.claude/rules/bash-harness-pitfalls.md` — "Silence is not success"). That matters more once
|
|
789
|
+
anything is wired to the mirror (e.g. a Vercel Git deploy): a silently-failing push means the
|
|
790
|
+
downstream artifact never updates and nobody is told.
|
|
791
|
+
|
|
792
|
+
Run it verbatim — `tests/skills/session-end/github-mirror-push.test.mjs` extracts the block
|
|
793
|
+
between the markers and executes it, so no second copy of this command may exist.
|
|
794
|
+
|
|
806
795
|
```bash
|
|
807
|
-
#
|
|
808
|
-
|
|
796
|
+
# --- github-mirror-push:begin ---
|
|
797
|
+
# Only attempt if 'mirror: github' is in Session Config.
|
|
798
|
+
# State 0: not a git repository at all → loud WARN, exit 1. This state was MISSED
|
|
799
|
+
# in the first version and is the reason it is listed first now: outside
|
|
800
|
+
# a repo, `git remote get-url` fails with "fatal: not a git repository",
|
|
801
|
+
# which is indistinguishable from "no such remote" by exit code alone.
|
|
802
|
+
# The block then announced "no 'github' remote configured — skipping
|
|
803
|
+
# (not an error)" and exited 0 — fail-open, in the very fix written to
|
|
804
|
+
# close a fail-open. Found by an adversarial reviewer, not by the author.
|
|
805
|
+
# State 1: no 'github' remote → informational, exit 0 (legitimate for consumer repos)
|
|
806
|
+
# State 2: push succeeded → confirmation WITH the pushed SHA, exit 0
|
|
807
|
+
# State 3: push FAILED → loud WARN on stderr WITH git's real output, exit 1
|
|
808
|
+
if ! git_dir=$(git rev-parse --git-dir 2>&1); then
|
|
809
|
+
echo "WARN GitHub mirror: not a git repository — cannot mirror anything." >&2
|
|
810
|
+
echo " git said: ${git_dir}" >&2
|
|
811
|
+
exit 1
|
|
812
|
+
elif ! mirror_url=$(git remote get-url github 2>&1); then
|
|
813
|
+
echo "GitHub mirror: no 'github' remote configured — skipping (not an error)."
|
|
814
|
+
echo " git said: ${mirror_url}" >&2
|
|
815
|
+
elif push_out=$(git push github HEAD 2>&1); then
|
|
816
|
+
echo "GitHub mirror: pushed $(git rev-parse HEAD) -> ${mirror_url}"
|
|
817
|
+
else
|
|
818
|
+
echo "WARN GitHub mirror PUSH FAILED: $(git rev-parse HEAD) is NOT on ${mirror_url}" >&2
|
|
819
|
+
echo "${push_out}" >&2
|
|
820
|
+
echo "WARN Mirror is stale — anything wired to it (site deploy) will not update." >&2
|
|
821
|
+
exit 1
|
|
822
|
+
fi
|
|
823
|
+
# --- github-mirror-push:end ---
|
|
809
824
|
```
|
|
810
825
|
|
|
826
|
+
State 3 exits non-zero on purpose: it is the only machine-readable signal that the mirror is
|
|
827
|
+
behind. Report it to the operator in the session summary; do not retry silently and do not
|
|
828
|
+
swallow it with `|| true`.
|
|
829
|
+
|
|
811
830
|
## Phase 4a: Auto-Promoted Worktree Cleanup (#575 P3.2)
|
|
812
831
|
|
|
813
832
|
> Skip if `persistence: false` in Session Config. Skip silently if the current worktree is NOT an Auto-promoted sibling (the common case).
|
|
@@ -185,4 +185,4 @@ Finalize session metrics by reading the wave data accumulated during execution:
|
|
|
185
185
|
> - `open_questions_asked` / `open_questions_answered` / `open_questions_deferred` (#773): the three open-question counts from the Phase 1.65 gate's AUQ Call 2 (identical to the `questions_*` payload fields on the `orchestrator.handover.gated` event). Top-level, additive, non-negative integers. Populate ONLY when the gate ran an interactive triage ("Closen + Triage" path). OMIT all three (do NOT write `0`) when the gate was skipped (fail-open / headless / disabled) or took the fast-path — absent = "not measured", `0` = "measured, zero questions". Validator accepts absent/null/non-negative-integer.
|
|
186
186
|
> - `stagnation_events`: populated ONLY when ≥1 stagnation event was logged to `events.jsonl` during this session. When `total == 0`, the field is omitted from the JSONL entry.
|
|
187
187
|
> - `grounding_injections`: populated ONLY when ≥1 `orchestrator.grounding.injected` event was logged to `events.jsonl` during this session. When `count == 0`, the field is omitted from the JSONL entry.
|
|
188
|
-
> - `memory_cleanup_at`:
|
|
188
|
+
> - `memory_cleanup_at`: **derived by the writer, not supplied by the coordinator.** `scripts/emit-session.mjs` sets it to `completed_at` whenever an `orchestrator.memory.cleanup_completed` event for THIS session sits in `events.jsonl` — emitted by every `/memory-cleanup` run in ANY mode (dry-run, apply-pending, OR healthy no-op). **A no-op is still a cleanup; it still emits, so the cadence marker (`readDreamSignals` → `lastCleanupAt`) still advances and `shouldDispatchAutoDream` does not fire a false nudge.** No event → field absent (never `null`). An explicit value already on the record wins and is not overwritten. Do NOT hand-call `stampMemoryCleanup()` here — the coordinator-supplied-boolean form was removed on 2026-08-17 after it silently failed for a real cleanup on 2026-08-14. (#699)
|
|
@@ -37,6 +37,33 @@ The proposals queue is populated mid-session by wave-executor agents calling `no
|
|
|
37
37
|
|
|
38
38
|
3. If `queue.length === 0`: log `memory-proposals: queue empty (stats: ${JSON.stringify(stats)})` and continue.
|
|
39
39
|
|
|
40
|
+
3b. **Relation judgment (#1016)** — enrich each queued proposal with its relation to the existing corpus, BEFORE step 4 renders its label. Without this, the operator approves a proposal without being told that the corpus already holds it, or holds its opposite.
|
|
41
|
+
|
|
42
|
+
> **Cadence contrast — read this before the step above and the step below.** Step 2's `collectProposals` and step 3's short-circuit run ONCE per session-end; step 4 batches ONCE per 4 items. **This step runs once per queued proposal.** The pool build is one call; the judgment is per candidate.
|
|
43
|
+
|
|
44
|
+
> **Cost, and where it may run.** The pool build is O(N²) over `queue.length + corpus.length` (~13 ms at N=100 records; viability boundary ~N=2000). Session-end and `/evolve` are the only two sanctioned call sites. Never from a wave dispatch, an inter-wave checkpoint, or a hook.
|
|
45
|
+
|
|
46
|
+
Skip when `.orchestrator/metrics/learnings.jsonl` is absent or holds fewer than 2 entries — with no corpus there is no relation to judge. Otherwise:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
import { buildCandidatePools } from '${PLUGIN_ROOT}/scripts/lib/learnings/candidates.mjs';
|
|
50
|
+
import { buildJudgmentInput, judgeCandidate, applyVerdict }
|
|
51
|
+
from '${PLUGIN_ROOT}/scripts/lib/learnings/judgment.mjs';
|
|
52
|
+
|
|
53
|
+
const { entries: corpus } = await readLearnings('.orchestrator/metrics/learnings.jsonl');
|
|
54
|
+
const { pools } = buildCandidatePools([...queue, ...corpus], { now: new Date() });
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`pools[]` is `{seed, candidates}` per seed — a bounded, per-seed, non-transitive neighbour set (a neighbour of a neighbour is not a neighbour; there is no clustering pass). For each pool whose `seed` is a QUEUE item (corpus-seeded pools are not this phase's business):
|
|
58
|
+
|
|
59
|
+
1. `buildJudgmentInput({ candidate: pool.seed, neighbours: pool.candidates.map((c) => c.record) })`. It returns `null` for a proposal with no usable `id` — leave that item's label bare and move on.
|
|
60
|
+
2. `judgeCandidate(input, { judge })`. `judge` is the injected verdict provider: 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; here the COORDINATOR is the judge and the coordinator holds the result).
|
|
61
|
+
3. `applyVerdict(verdict, effects)` — the single choke point where a judgment may become an effect. In this phase every handler (`refine`, `supersede`, `merge`, `proposeContradiction`) records the relation onto the queue item so step 4 can render it. **None of them writes to disk here**; the only write this phase performs is step 6's `promoteAndClear()`, on the operator's selection.
|
|
62
|
+
|
|
63
|
+
**Fail closed — a voided judgment never reaches the operator.** `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 that none exists. `applyVerdict` refuses the whole batch — including `proposeContradiction`, the AUQ renderer, because rendering a relation from an unreadable judgment IS the claim. The item then falls through to step 4 with its ordinary bare label, exactly as before #1016. Never substitute a default decision, never repair-retry, never surface the failure mode as if it were a verdict. A judge error is logged (`memory-proposals: judgment voided for <id> (${verdict.failureMode})`) and never blocks the close.
|
|
64
|
+
|
|
65
|
+
**Label enrichment (step 4 input).** A proposal carrying a relation renders as `[<type-12>] | <subject-40> | conf=X.XX | <decision> <n>` (e.g. `contradict 1`, `merge 2`) with the judgment's `rationale` leading the option description. A proposal with no relation — `skip`, `abstain`, no pool, or a voided verdict — renders exactly as it does today. The operator's selection remains the only gate; the judgment supplies the relation, never the decision.
|
|
66
|
+
|
|
40
67
|
4. **AUQ pagination logic**: partition the queue into FIFO batches of 4 inline:
|
|
41
68
|
|
|
42
69
|
- Empty queue → silent skip (no AUQ rendered).
|
|
@@ -81,11 +108,13 @@ The proposals queue is populated mid-session by wave-executor agents calling `no
|
|
|
81
108
|
|
|
82
109
|
- Spec: issue #501 — memory-proposals (F2.1); no standalone PRD file
|
|
83
110
|
- Modules: `scripts/lib/memory-proposals/{schema,store,collector,sink}.mjs`
|
|
111
|
+
- Relation judgment (step 3b, #1016): `scripts/lib/learnings/candidates.mjs` (`buildCandidatePools`) · `scripts/lib/learnings/judgment.mjs` (`buildJudgmentInput`, `judgeCandidate`, `applyVerdict`, `JUDGMENT_DECISIONS`, `FAILURE_MODES`)
|
|
84
112
|
- CLI: `scripts/memory-propose.mjs` (agents call this)
|
|
85
113
|
- Hook: `hooks/pre-bash-memory-propose-audit.mjs` (audit trail)
|
|
86
114
|
- Coordinator AUQ spec: `agents/memory-proposal-collector.md` (reference doc)
|
|
87
115
|
- Sibling phases: 3.6.5 Auto-Dream (#502), 3.6.6 Skill-Applied Judge (#645 L3), 3.6.7 Auto-Dialectic (#506)
|
|
88
|
-
-
|
|
116
|
+
- Sibling call site of the same judgment pair: `skills/evolve/SKILL.md` § Step 3.3b (the `/evolve` producer for the `-0.2 if contradicted` branch)
|
|
117
|
+
- Issues: #501 (this phase), #1016 (step 3b)
|
|
89
118
|
|
|
90
119
|
### 3.6.4 Expired-Learnings Sweep (Advisory — Epic #723 B4)
|
|
91
120
|
|
|
@@ -70,7 +70,7 @@ Compare the files the plan said would be touched against the files actually chan
|
|
|
70
70
|
- Create a VCS issue for the remaining work with:
|
|
71
71
|
- Title: `[Carryover] <original task description>`
|
|
72
72
|
- Labels: `priority::<original>`, `status:ready`
|
|
73
|
-
- Description: what's done, what's left, context for next session
|
|
73
|
+
- Description: what's done, what's left, context for next session, Revisit-Trigger (mandatory — a concrete reopen condition; a deferral with no named trigger is not a deferral; see `skills/gitlab-ops/SKILL.md § Carryover Template`)
|
|
74
74
|
- Link to original issue if applicable
|
|
75
75
|
|
|
76
76
|
### 1.3 Not Started Items
|
|
@@ -78,10 +78,6 @@ Compare the files the plan said would be touched against the files actually chan
|
|
|
78
78
|
- If still relevant: ensure original issue remains `status:ready`
|
|
79
79
|
- If no longer relevant: close with comment explaining why
|
|
80
80
|
|
|
81
|
-
### 1.3a Optional /goal Backlog-Drain (opt-in — #636)
|
|
82
|
-
|
|
83
|
-
When `goal-integration.enabled: true` with seam `session-end-backlog`, the close may surface ONE advisory `/goal` command to drain still-relevant §1.2/§1.3 items in-session instead of carrying them over. See `SKILL.md § 1.3a Optional /goal Backlog-Drain` for the full gate conditions, advisory-only contract, and the LM-008 cross-reference — the two files mirror each other; the prose lives in SKILL.md.
|
|
84
|
-
|
|
85
81
|
### 1.4 Emergent Work
|
|
86
82
|
- Tasks that were NOT in the plan but were done (fixes, discoveries)
|
|
87
83
|
- Document and attribute to relevant issues
|
|
@@ -19,19 +19,13 @@
|
|
|
19
19
|
|
|
20
20
|
1. Ensure `.orchestrator/metrics/` directory exists: `mkdir -p .orchestrator/metrics`
|
|
21
21
|
|
|
22
|
-
1-pre. **`memory_cleanup_at`
|
|
22
|
+
1-pre. **`memory_cleanup_at` is DERIVED, not remembered (#699 + 2026-08-17 follow-up)** — there is **no coordinator step here any more**. Do not set a `ranMemoryCleanupThisSession` boolean and do not call `stampMemoryCleanup()` by hand at session-end.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
import { stampMemoryCleanup } from '../../scripts/lib/memory-cleanup-stamp.mjs';
|
|
24
|
+
`scripts/emit-session.mjs` derives the field itself: it calls `deriveMemoryCleanupSignal()` (`scripts/lib/memory-cleanup-stamp.mjs`), which reads the sibling `events.jsonl` for `orchestrator.memory.cleanup_completed` records whose `timestamp` falls inside this session's own `[started_at, completed_at]` window (and whose `semantic_session_id`, when present, matches). The emitting side is the LAST step of every `/memory-cleanup` run — see `skills/memory-cleanup/SKILL.md` § "Session-End Signal".
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
metricsEntry = stampMemoryCleanup(metricsEntry, {
|
|
29
|
-
ranCleanup: ranMemoryCleanupThisSession, // boolean
|
|
30
|
-
completedAt: metricsEntry.completed_at,
|
|
31
|
-
});
|
|
32
|
-
```
|
|
26
|
+
**Contract:** a no-op run (MEMORY.md already healthy, no files mutated) is still a cleanup, so it still emits and therefore still stamps. When `/memory-cleanup` did not run, no event exists, nothing is derived, and the field is simply absent — never `null`. An EXPLICIT `memory_cleanup_at` already present on the record WINS over derivation and is never overwritten; that path is for backfills and tests, not for normal operation.
|
|
33
27
|
|
|
34
|
-
**
|
|
28
|
+
**Why this stopped being a coordinator instruction.** It was one until 2026-08-17, and it measurably failed: a `/memory-cleanup` ran on 2026-08-14 with a documented yield, the prose step above was not executed, and all three session records of that day carried `memory_cleanup_at: null` — so the session-start banner reported "last cleanup 29 days ago" against the operator's own "3 days". `stampMemoryCleanup()` had **zero production callers** at that point; every reference to it was an instruction asking an LLM to remember. Same failure class as the STATE.md write-race Epic #583 replaced with a lock: Disziplin statt Mechanik.
|
|
35
29
|
|
|
36
30
|
> **#701.2 DOC NOTE — `completed_at >= started_at` guard:** This invariant is enforced mechanically by `scripts/emit-session.mjs`. The writer applies `clampTimestampsMonotonic()` (from `scripts/lib/session-schema/timestamps.mjs`) before `validateSession()`, clamping any inversion of `completed_at < started_at` to `started_at` and recording forensics in `_clamped: true` / `_original_completed_at`. Previously-inverted entries (e.g. `main-2026-06-21-session-4`) are already corrected. **No per-session coordinator action is needed** — the writer enforces the invariant at write time. Do not add defensive clamping logic here; the canonical guard lives in `emit-session.mjs`.
|
|
37
31
|
|
|
@@ -39,6 +33,8 @@
|
|
|
39
33
|
|
|
40
34
|
**Semantics:** `null` totals mean "no token data was captured for this session" — this is NOT the same as zero cost. Do NOT coerce null to 0 when displaying or summing across sessions.
|
|
41
35
|
|
|
36
|
+
**Provenance (#949):** the rollup sums ONLY records carrying `subagent_transcript_found: true` — the flag the producer sets when it read the subagent's own transcript. Pre-#949 records carry the PARENT transcript's running totals and are excluded, so a session made up entirely of them now reports `null` rather than a fabricated sum (73 historical sessions, 96,148,781 phantom tokens, measured 2026-08-11). Two consequences for readers: totals already written into `sessions.jsonl` before 2026-08-11 were produced by the unfiltered recipe and are a series break, not a trend; and `matched_records` counts start records and phantom stops alike, so it is NOT the denominator for a coverage ratio — use `subagents_with_tokens` against the session's real agent count.
|
|
37
|
+
|
|
42
38
|
Example (coordinator pseudo-code — adapt to your shell/JS context):
|
|
43
39
|
|
|
44
40
|
```js
|
|
@@ -275,7 +275,7 @@ mission-status:
|
|
|
275
275
|
- `status`: always `brainstormed` at plan emission. Terminal values are updated at gate transitions by wave-executor: `brainstormed` → `validated` (user confirms via `/go`) → `in-dev` (agent dispatched) → `testing` (Quality wave) → `completed` (Quality gate green). session-end Phase 1.9 reads the current value to classify the item.
|
|
276
276
|
|
|
277
277
|
**Transition gates (summary):**
|
|
278
|
-
At plan time, all items start at `brainstormed`. When the user runs `/go` to approve the plan, wave-executor updates each item to `validated`. When an agent for a wave-plan item is dispatched, wave-executor updates that item to `in-dev`. When the Quality wave begins, items from prior waves move to `testing`. When the Quality gate passes, items finalize at `completed`. Rollback to `brainstormed` is permitted from any state.
|
|
278
|
+
At plan time, all items start at `brainstormed`. When the user runs `/go` to approve the plan, wave-executor updates each item to `validated`. When an agent for a wave-plan item is dispatched, wave-executor updates that item to `in-dev`. When the Quality wave begins, items from prior waves move to `testing`. When the Quality gate passes, items finalize at `completed`. Rollback to `brainstormed` is permitted from any state. This ordering is **coordinator convention, not a mechanical gate** — nothing validates a transition before it is written (see "Default and transitions" below).
|
|
279
279
|
|
|
280
280
|
**Omission rule:** When the plan has 0 wave-plan items (e.g., pure express-path coord-direct with no sub-agent tasks), do NOT emit the `### Wave-Plan Mission Status (machine-readable)` block.
|
|
281
281
|
|
|
@@ -298,7 +298,7 @@ Every wave-plan item carries a `status` field drawn from a 5-value enum. The fie
|
|
|
298
298
|
- **Default at plan creation:** `brainstormed` — all items start here.
|
|
299
299
|
- **Transitions are coordinator-level orchestration** (not inside individual agent prompts). See `skills/wave-executor/SKILL.md` "Mission-Status Updates (#340)" for when each transition fires.
|
|
300
300
|
- **Rollback:** any item may return to `brainstormed` from any state (e.g. if work is discarded or re-planned).
|
|
301
|
-
- **
|
|
301
|
+
- **No mechanical validation — by design.** The `status` values come from the 5-value enum in the table above, but nothing checks a transition before it is written. `setMissionStatus` (`scripts/lib/state-md/mission-status.mjs`) mirrors whatever string it is handed onto BOTH the body section and the frontmatter array, deliberately without an enum gate: gating it would reintroduce the exact body-says-X/frontmatter-says-Y divergence that sync exists to remove. An out-of-enum value therefore lands visibly on both surfaces instead of being silently rejected on one. Keeping the enum honest is the coordinator's job.
|
|
302
302
|
|
|
303
303
|
#### Status field in wave-plan items
|
|
304
304
|
|
|
@@ -18,7 +18,7 @@ For each wave, define agents with:
|
|
|
18
18
|
|
|
19
19
|
- `Isolation: worktree` means the wave-executor will pass `isolation: "worktree"` to the Agent tool, giving each agent its own git worktree copy
|
|
20
20
|
- `MaxTurns` is enforced via the agent prompt — wave-executor includes a turn limit instruction in each agent's prompt
|
|
21
|
-
- `status` is the mission-status enum value for this wave-plan item (#340). Always `brainstormed` in the initial plan. Wave-executor updates it at gate transitions (validated → in-dev → testing → completed).
|
|
21
|
+
- `status` is the mission-status enum value for this wave-plan item (#340). Always `brainstormed` in the initial plan. Wave-executor updates it at gate transitions (validated → in-dev → testing → completed). Rollback to `brainstormed` is allowed from any state. The five values are listed in `SKILL.md` § Mission-Status Enum; nothing validates them mechanically — `setMissionStatus` writes the string it is given to both STATE.md surfaces on purpose, so keeping the value in-enum is the coordinator's job.
|
|
22
22
|
|
|
23
23
|
> **Deconfliction rule:** Before finalizing agent specs for a wave, verify that no two agents in the same wave list overlapping `Files:` paths. If overlap is found, either merge the agents into one or move one task to a later wave. Two agents editing the same file in parallel causes merge conflicts that require manual resolution.
|
|
24
24
|
|
|
@@ -21,6 +21,8 @@ description: >
|
|
|
21
21
|
|
|
22
22
|
Before anything else, read and internalize `soul.md` in this skill directory. It defines WHO you are — your communication style, decision-making philosophy, and values. Every interaction in this session should reflect this identity. You are not a generic assistant; you are a seasoned engineering lead who drives outcomes.
|
|
23
23
|
|
|
24
|
+
**Then set the output level.** Read `~/.config/session-orchestrator/owner.yaml` and take `efficiency.output-level` (`lite` | `full` | `ultra`), `efficiency.preamble`, and `tone.style`. If the file is missing, unreadable, or a key is absent, use the defaults `full` / `minimal` / `neutral`. Apply the matching `### output-level: <value>` block from `soul.md` § Output Levels for the whole session — its line budgets are binding, not advisory, and § "Never traded for brevity" names what they may never cut.
|
|
25
|
+
|
|
24
26
|
## Phase 0: Bootstrap Gate
|
|
25
27
|
|
|
26
28
|
Read `skills/_shared/bootstrap-gate.md` and execute the gate check. If the gate is CLOSED, invoke `skills/bootstrap/SKILL.md` and wait for completion before proceeding. If the gate is OPEN, continue to Phase 1.
|
|
@@ -783,7 +785,19 @@ Group issues by:
|
|
|
783
785
|
|
|
784
786
|
Non-blocking. Cross-reference: `scripts/lib/ci-status-banner.mjs` (the sibling project-facing probe) and `.claude/rules/test-value.md` § TV-005 (why structural gates beat unit-test volume).
|
|
785
787
|
|
|
786
|
-
|
|
788
|
+
Additionally, invoke the mirror-issues probe (`scripts/lib/mirror-issues-banner.mjs`) via `await checkMirrorIssues({ repoRoot })`. This is the only probe that deliberately queries the platform the session did NOT auto-detect. `skills/gitlab-ops/SKILL.md` § VCS Auto-Detection selects exactly one platform via if/else, so in a repo whose `origin` is GitLab and whose `github` remote is a public mirror, no code path ever reads the mirror's issue tracker — issues filed there by external reporters are structurally invisible to every session. The VCS family is therefore hard-pinned to `'github'` inside the module rather than auto-detected. It takes no Session Config key: `resolveRepoSpec({ repoRoot, vcs: 'github' })` derives the `gh -R` spec from `git remote`, which makes the probe self-disabling — a repo with no GitHub mirror resolves to `undefined`, returns `null`, and spawns no subprocess.
|
|
789
|
+
|
|
790
|
+
The return contract has THREE states, not the usual two, and the third is the point: `null` means either "no mirror remote" or "queried successfully, zero open issues"; `{ severity, message, count, repoSpec, issues }` means N > 0; and `{ severity, message, repoSpec, degraded }` means the query did NOT succeed, where `degraded` is one of `cli-missing | timeout | parse-error | auth-error | query-failed`. Render `result.message` verbatim in either non-null case. A `degraded` result must be read as *"the mirror's state is unknown"* — never as clean. `scripts/lib/ci-status-banner.mjs` collapses all three of missing-CLI, unparseable output and absent-remote onto `null`, which in the banner contract reads as "all clear"; that collapse is why this gap survived unseen. Do not reproduce it.
|
|
791
|
+
|
|
792
|
+
Additionally, invoke the git-config-drift probe (`scripts/lib/git-config-drift.mjs`) via `checkGitConfigDrift({ repoRoot })` (synchronous — no await; `env` defaults to `process.env`). It reads `git config --local --list` with a FILTERED environment, so an ambient `GIT_DIR` cannot redirect the probe itself at a foreign repository and let it call this one clean. **Three states, not two:** `null` = read and clean; `{ severity: 'warn', message, findings }` = at least one unexpected entry (a local identity override, a local `commit.gpgsign`, a remote on a reserved fixture host, a `core.hooksPath` not pointing at `.husky/_`, or `GIT_DIR`/`GIT_WORK_TREE` set in the environment); `{ …, degraded }` = the config could NOT be read — **never render that as clean.** Render `result.message` alongside the other banners.
|
|
793
|
+
|
|
794
|
+
This is the only probe that inspects `.git/config`, and that is the whole point: `git status` cannot see that file. On 2026-08-19 a coordinator diagnostic exported `GIT_DIR` at this repository while the suite ran; test fixtures wrote a foreign remote and their own `user.email`/`user.name` into the local config, and the identity then authored two commits that reached both remotes. A recovery pass checked HEAD, the index and all 1614 tracked files, found everything clean, and missed it — because none of those surfaces show `.git/config`. It surfaced two hours later, from an agent measuring something else.
|
|
795
|
+
|
|
796
|
+
The complementary halves live elsewhere and are not duplicates of this probe: `tests/setup/scrub-git-env.mjs` (wired via `setupFiles` in `vitest.config.mjs`) removes the redirecting variables before any test runs, and `scripts/lib/validate/check-test-git-config-target.mjs` censuses untargeted state-mutating git calls in `tests/**`. The census is WARN-only by measurement — its first cut was 11 hits, all false positives — and it explicitly reports `gitDirInheritable`, the population it cannot close, because the incident's own call sites passed a correct `cwd` and were redirected anyway.
|
|
797
|
+
|
|
798
|
+
Non-blocking. Cross-reference: `scripts/lib/vcs-repo-spec.mjs` (`isQueryFailure` — the same absence-vs-query-failure split this probe's `degraded` state implements).
|
|
799
|
+
|
|
800
|
+
All banners are non-blocking — display in the Session Overview, do not halt the session. If `bootstrap-lock-freshness.mjs` is absent (pre-#186 plugin install) or `peer-cards/staleness-banner.mjs` is absent (pre-#503 plugin install) or `loop-readiness-banner.mjs` is absent (pre-#633 plugin install) or `instruction-budget-guard.mjs` is absent (pre-#687 plugin install) or `reconcile-nudge-banner.mjs` is absent (pre-#723 plugin install) or `sessions-staleness-banner.mjs` is absent (pre-#724 plugin install) or `sessions-integrity-banner.mjs` is absent (pre-#958 plugin install) or `owner-config-banner.mjs` is absent (pre-#820 plugin install) or `moc-staleness-banner.mjs` / `context-coverage-banner.mjs` are absent (pre-#831 plugin install) or `claude-md-budget-lint.mjs` is absent (pre-#878 plugin install) or `mirror-issues-banner.mjs` is absent (pre-#1022 plugin install), skip silently.
|
|
787
801
|
|
|
788
802
|
## Phase 4.5: Resource Health (v3.1.0)
|
|
789
803
|
|