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
|
@@ -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/SKILL.md
CHANGED
|
@@ -281,17 +281,17 @@ If any criterion is FAIL:
|
|
|
281
281
|
2. Revise the affected PRD sections
|
|
282
282
|
3. Re-submit to the reviewer
|
|
283
283
|
|
|
284
|
-
Maximum 3 iterations. After 3 iterations with remaining issues,
|
|
284
|
+
Maximum 3 iterations. After 3 iterations with remaining issues, list the flagged points in plain text (they are context, not a choice), then ask via AskUserQuestion:
|
|
285
285
|
|
|
286
286
|
```
|
|
287
287
|
AskUserQuestion({
|
|
288
288
|
questions: [{
|
|
289
|
-
question: "The
|
|
289
|
+
question: "The reviewer still flags [N] points after 3 revision rounds. Proceed anyway?",
|
|
290
290
|
header: "PRD Review",
|
|
291
291
|
options: [
|
|
292
|
-
{ label: "Accept as-is (Recommended)", description: "
|
|
293
|
-
{ label: "Manual edit", description: "
|
|
294
|
-
{ label: "Re-run review", description: "
|
|
292
|
+
{ label: "Accept as-is (Recommended)", description: "Three rounds did not close them, so a fourth probably will not. The flags then stay in the PRD and travel into the issues filed from it." },
|
|
293
|
+
{ label: "Manual edit", description: "You edit the PRD yourself; the flow waits, then re-runs the reviewer on your version." },
|
|
294
|
+
{ label: "Re-run review", description: "One more revision round. Cost: another reviewer pass, and the same points may come back unchanged." }
|
|
295
295
|
],
|
|
296
296
|
multiSelect: false
|
|
297
297
|
}]
|
|
@@ -300,16 +300,16 @@ AskUserQuestion({
|
|
|
300
300
|
|
|
301
301
|
### 5.3 User Review Gate
|
|
302
302
|
|
|
303
|
-
After the reviewer passes (or user accepts), present the final PRD:
|
|
303
|
+
After the reviewer passes (or user accepts), present the final PRD in plain text — path plus a short section summary — then ask:
|
|
304
304
|
|
|
305
305
|
```
|
|
306
306
|
AskUserQuestion({
|
|
307
307
|
questions: [{
|
|
308
|
-
question: "
|
|
308
|
+
question: "Approve the PRD at [path]?",
|
|
309
309
|
header: "PRD Approval",
|
|
310
310
|
options: [
|
|
311
|
-
{ label: "Approve PRD (Recommended)", description: "PRD
|
|
312
|
-
{ label: "Request changes", description: "
|
|
311
|
+
{ label: "Approve PRD (Recommended)", description: "Nothing further is checked after this: approval commits the PRD to HEAD (Phase 5.5), then issue creation starts." },
|
|
312
|
+
{ label: "Request changes", description: "Describe what to change; the PRD is rewritten and comes back here. No limit on rounds." }
|
|
313
313
|
],
|
|
314
314
|
multiSelect: false
|
|
315
315
|
}]
|
|
@@ -395,18 +395,20 @@ Assign labels from the standard taxonomy:
|
|
|
395
395
|
|
|
396
396
|
### 6.3 User Review
|
|
397
397
|
|
|
398
|
-
Present the full issue structure via AskUserQuestion before creating anything:
|
|
398
|
+
Present the full issue structure via AskUserQuestion before creating anything. The table is the text that is about to be filed, so it belongs in `preview` — not in the question:
|
|
399
399
|
|
|
400
400
|
```
|
|
401
401
|
AskUserQuestion({
|
|
402
402
|
questions: [{
|
|
403
|
-
question: "
|
|
404
|
-
header: "
|
|
403
|
+
question: "Create these [N] issues from the PRD?",
|
|
404
|
+
header: "Issues",
|
|
405
405
|
options: [
|
|
406
|
-
{ label: "Create all
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
{ label: "
|
|
406
|
+
{ label: "Create all [N] (Recommended)",
|
|
407
|
+
description: "Priorities and blocked-by links come straight from the approved PRD. Cost: one API call per issue, ~1s apart.",
|
|
408
|
+
preview: "**Epic:** [title]\n\n| # | Sub-Issue | Priority | Labels | Blocked By |\n|---|----------|----------|--------|------------|\n| 1 | [title] | critical | [labels] | — |\n| 2 | [title] | high | [labels] | #1 |\n| ... | ... | ... | ... | ... |" },
|
|
409
|
+
{ label: "Adjust priorities", description: "Same [N] issues, different priority labels. Name them and this question comes back with the table updated." },
|
|
410
|
+
{ label: "Remove issues", description: "Name the ones to drop; the rest are created unchanged." },
|
|
411
|
+
{ label: "Cancel", description: "Nothing is created. The PRD stays committed, so Phase 6 can run again later." }
|
|
410
412
|
],
|
|
411
413
|
multiSelect: false
|
|
412
414
|
}]
|
|
@@ -127,7 +127,7 @@ Apply per gitlab-ops skill label taxonomy:
|
|
|
127
127
|
|
|
128
128
|
### User Review Gate
|
|
129
129
|
|
|
130
|
-
Present the full issue structure via AskUserQuestion
|
|
130
|
+
Present the full issue structure via the AskUserQuestion payload in `SKILL.md` § 6.3 — the issue table belongs in the option's `preview` field, not in the question text:
|
|
131
131
|
|
|
132
132
|
- Epic title and description
|
|
133
133
|
- Each sub-issue: title, priority, labels, dependency links
|
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** —
|
|
41
|
+
6. **GitLab group** — Select the GitLab host explicitly, then 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 --hostname "$GITLAB_HOST" "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,17 @@ 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
|
|
137
|
+
|
|
138
|
+
# These values identify the NEW project, not the directory in which this plan runs.
|
|
139
|
+
# Select the host with the group; do not let glab infer it from an ambient remote.
|
|
140
|
+
GITLAB_HOST="<selected GitLab hostname>"
|
|
141
|
+
PROJECT_PATH="$GROUP_PATH/$PROJECT_NAME"
|
|
142
|
+
ENCODED_PROJECT_PATH="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$PROJECT_PATH")"
|
|
132
143
|
(
|
|
133
144
|
echo "$TYPE_CHOICE" # e.g., "1" for nextjs-saas
|
|
134
145
|
echo "$STYLE_CHOICE" # e.g., "1" for vega (only if nextjs-saas)
|
|
@@ -140,10 +151,12 @@ Map gathered answers to script input choices:
|
|
|
140
151
|
|
|
141
152
|
### Step 2: Verify success
|
|
142
153
|
|
|
143
|
-
Check exit code. Confirm
|
|
154
|
+
Check the setup script exit code. Confirm the selected path exists without fetching a full REST project object:
|
|
144
155
|
|
|
145
156
|
```bash
|
|
146
|
-
glab
|
|
157
|
+
glab api --hostname "$GITLAB_HOST" graphql \
|
|
158
|
+
-f query='query($fullPath: ID!) { project(fullPath: $fullPath) { fullPath } }' \
|
|
159
|
+
-f fullPath="$PROJECT_PATH" | jq -er '.data.project.fullPath'
|
|
147
160
|
```
|
|
148
161
|
|
|
149
162
|
### Step 3: Adjust visibility
|
|
@@ -151,15 +164,30 @@ glab repo view $GROUP/$PROJECT_NAME
|
|
|
151
164
|
If visibility is not `internal` (the default):
|
|
152
165
|
|
|
153
166
|
```bash
|
|
154
|
-
glab repo edit
|
|
167
|
+
# There is no `glab repo edit`, and `glab repo update` carries no --visibility
|
|
168
|
+
# flag (its FLAGS are --archive/--defaultBranch/-d/--description). The encoded
|
|
169
|
+
# endpoint and explicit host target the new project independently of the CWD.
|
|
170
|
+
glab api --silent --hostname "$GITLAB_HOST" -X PUT \
|
|
171
|
+
"projects/${ENCODED_PROJECT_PATH}" \
|
|
172
|
+
-f visibility=private # or visibility=public
|
|
173
|
+
|
|
174
|
+
# Fetch only the scalar needed to verify the mutation, never a full REST object.
|
|
175
|
+
glab api --hostname "$GITLAB_HOST" graphql \
|
|
176
|
+
-f query='query($fullPath: ID!) { project(fullPath: $fullPath) { visibility } }' \
|
|
177
|
+
-f fullPath="$PROJECT_PATH" | jq -er '.data.project.visibility'
|
|
155
178
|
```
|
|
156
179
|
|
|
180
|
+
> The GraphQL verification query is read-only and requests only `visibility`; the
|
|
181
|
+
> PUT's unused response is intentionally suppressed. Confirm the selected host and
|
|
182
|
+
> path before running the mutation.
|
|
183
|
+
|
|
157
184
|
For public/OSS, also configure GitHub mirror if applicable.
|
|
158
185
|
|
|
159
186
|
### Step 4: Set branch protection
|
|
160
187
|
|
|
161
188
|
```bash
|
|
162
|
-
glab api -X
|
|
189
|
+
glab api --silent --hostname "$GITLAB_HOST" -X POST \
|
|
190
|
+
"projects/${ENCODED_PROJECT_PATH}/protected_branches" \
|
|
163
191
|
-f name=main \
|
|
164
192
|
-f push_access_level=30 \
|
|
165
193
|
-f merge_access_level=30
|
|
@@ -264,7 +292,7 @@ Always use the `priority::<level>` format in VCS CLI commands, not P0/P1/P2/P3.
|
|
|
264
292
|
|
|
265
293
|
### Step 4: Present for user confirmation
|
|
266
294
|
|
|
267
|
-
Use AskUserQuestion
|
|
295
|
+
Use the AskUserQuestion payload in `SKILL.md` § 6.3 verbatim — the issue table belongs in the option's `preview` field, not in the question text:
|
|
268
296
|
|
|
269
297
|
- Epic title and description
|
|
270
298
|
- Sub-issues with: title, priority, labels, dependency links
|
|
@@ -274,11 +302,11 @@ Use AskUserQuestion to present the full issue structure:
|
|
|
274
302
|
|
|
275
303
|
```bash
|
|
276
304
|
# Create epic
|
|
277
|
-
glab issue create --title "$EPIC_TITLE" --description "$EPIC_DESC" \
|
|
305
|
+
glab issue create -R "https://${GITLAB_HOST}/${PROJECT_PATH}" --title "$EPIC_TITLE" --description "$EPIC_DESC" \
|
|
278
306
|
--label "type:epic,priority::$PRIORITY" --milestone "$MILESTONE"
|
|
279
307
|
|
|
280
308
|
# Create sub-issues
|
|
281
|
-
glab issue create --title "$ISSUE_TITLE" --description "$ISSUE_DESC" \
|
|
309
|
+
glab issue create -R "https://${GITLAB_HOST}/${PROJECT_PATH}" --title "$ISSUE_TITLE" --description "$ISSUE_DESC" \
|
|
282
310
|
--label "type:feature,priority::$PRIORITY,status:ready,area:$AREA,appetite:$APPETITE"
|
|
283
311
|
```
|
|
284
312
|
|
|
@@ -287,10 +315,12 @@ glab issue create --title "$ISSUE_TITLE" --description "$ISSUE_DESC" \
|
|
|
287
315
|
For issues with technical dependencies, set `blocks`/`is-blocked-by` relationships:
|
|
288
316
|
|
|
289
317
|
```bash
|
|
290
|
-
# Issue #2 is blocked by Issue #1
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
318
|
+
# Issue #2 is blocked by Issue #1 in this project. An encoded project path is
|
|
319
|
+
# valid for target_project_id, so no numeric project ID is needed.
|
|
320
|
+
glab api --silent --hostname "$GITLAB_HOST" -X POST \
|
|
321
|
+
"projects/${ENCODED_PROJECT_PATH}/issues/${ISSUE_2_IID}/links" \
|
|
322
|
+
-f target_project_id="$ENCODED_PROJECT_PATH" \
|
|
323
|
+
-f target_issue_iid="$ISSUE_1_IID" \
|
|
294
324
|
-f link_type=is_blocked_by
|
|
295
325
|
```
|
|
296
326
|
|
package/skills/plan/soul.md
CHANGED
|
@@ -69,6 +69,18 @@ The active level is `efficiency.output-level` in `~/.config/session-orchestrator
|
|
|
69
69
|
- Shape: name the alternatives you rejected and why, spell out the appetite and the scope cuts, define unfamiliar terms on first use.
|
|
70
70
|
- Escalation: `expand <topic>` — see § Escalation above.
|
|
71
71
|
|
|
72
|
+
### Register — how a sentence reads
|
|
73
|
+
|
|
74
|
+
The budgets above set *how much* you say; the register sets *how*. It is
|
|
75
|
+
defined once, in `skills/session-start/soul.md` § "Register — how a sentence
|
|
76
|
+
reads", and binds here unchanged: the frame ("write for someone who knows this
|
|
77
|
+
project but has not seen what you just saw"), the plain-words test with its
|
|
78
|
+
five worked cases, and its precedence over § "Never traded for brevity" above.
|
|
79
|
+
Read it there. It is not repeated here on purpose — the § Output Levels intro
|
|
80
|
+
sentence already exists in four copies across the four souls with nothing
|
|
81
|
+
checking their parity, and a fifth copied rule would drift the same way. A
|
|
82
|
+
pointer cannot.
|
|
83
|
+
|
|
72
84
|
### Companion dials
|
|
73
85
|
|
|
74
86
|
Same file, same lookup, same fallback-to-default rule:
|
|
@@ -243,12 +243,12 @@ For each batch (proposals sliced into groups of 4):
|
|
|
243
243
|
```
|
|
244
244
|
AskUserQuestion({
|
|
245
245
|
questions: [{
|
|
246
|
-
question: "
|
|
247
|
-
header: "
|
|
246
|
+
question: "Batch K of N — which rule proposals should be written into .claude/rules/?",
|
|
247
|
+
header: "Regeln",
|
|
248
248
|
options: [
|
|
249
249
|
{
|
|
250
250
|
label: "<slug>.md (confidence: 0.72)",
|
|
251
|
-
description: "
|
|
251
|
+
description: "From learning <learningKey>. Becomes a file under .claude/rules/ — where this repo keeps its rules. Text: <first 100 chars of rendered content>"
|
|
252
252
|
},
|
|
253
253
|
...up to 4 options per batch...
|
|
254
254
|
{
|
|
@@ -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
|
---
|
|
@@ -141,7 +141,8 @@ For every `SPIRAL` or `FAILED` agent surfaced in the walk above, ALSO append a c
|
|
|
141
141
|
```js
|
|
142
142
|
import { appendWhatNotToRetryOnDisk } from '${PLUGIN_ROOT}/scripts/lib/state-md.mjs';
|
|
143
143
|
|
|
144
|
-
// `parsed` = parseStateMd(STATE.md); session
|
|
144
|
+
// `parsed` = parseStateMd(STATE.md); `session:` is an attribution/history label.
|
|
145
|
+
// It records this entry's provenance only and never authorizes lock ownership.
|
|
145
146
|
const sessionId = parsed.frontmatter.session ?? 'unknown-session';
|
|
146
147
|
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
|
|
147
148
|
|
|
@@ -646,7 +647,7 @@ import { planTailPhases } from '${PLUGIN_ROOT}/scripts/lib/session-end/phase-ski
|
|
|
646
647
|
const { plan, skippedReport } = await planTailPhases({
|
|
647
648
|
repoRoot: process.cwd(),
|
|
648
649
|
config, // parsed Session Config (from $CONFIG)
|
|
649
|
-
sessionId, // session.lock `session_id`
|
|
650
|
+
sessionId, // physical session.lock `session_id` only (or null), never STATE.md `session`
|
|
650
651
|
platform, // 'claude' | 'codex' | 'cursor'
|
|
651
652
|
});
|
|
652
653
|
// plan: Array<{ phase, run, reason, inputSource }>, already in ascending phase order.
|
|
@@ -733,20 +734,21 @@ After STATE.md is finalized with `status: completed` (Phase 3.4) and Recommendat
|
|
|
733
734
|
|
|
734
735
|
```javascript
|
|
735
736
|
import { release } from 'scripts/lib/session-lock.mjs';
|
|
736
|
-
// sessionId
|
|
737
|
-
//
|
|
738
|
-
//
|
|
739
|
-
const
|
|
737
|
+
// sessionId is the physical raw value established by session-start Phase 1.2
|
|
738
|
+
// and stored in .orchestrator/session.lock `session_id`. It is not STATE.md
|
|
739
|
+
// `session:` or `semantic_session_id`, both of which are attribution labels.
|
|
740
|
+
const rawSessionId = sessionId;
|
|
741
|
+
const result = release({ sessionId: rawSessionId, repoRoot: process.cwd() });
|
|
740
742
|
// result.ok is always true unless a filesystem error occurred.
|
|
741
743
|
// result.deleted === true → lock file removed successfully.
|
|
742
|
-
// result.deleted === false → lock was absent or
|
|
744
|
+
// result.deleted === false → lock was absent or had a different raw session_id.
|
|
743
745
|
```
|
|
744
746
|
|
|
745
|
-
If `result.deleted === false`, log `info: session-lock not released — already absent or session_id mismatch
|
|
747
|
+
If `result.deleted === false`, log `info: session-lock not released — already absent or raw session_id mismatch` and continue. An active lock whose raw id differs is ambiguous: do **not** retry release with an equal `semantic_session_id`, STATE.md `session`, or owner proof. Leave that live lock for its TTL/Reaper lifecycle.
|
|
746
748
|
|
|
747
749
|
If `result.ok === false` (rare filesystem error), log `⚠ session-lock: release failed — <result.reason>` and continue. Do NOT block the close for a lock-release failure — the TTL provides automatic expiry for the next session.
|
|
748
750
|
|
|
749
|
-
The lock is released here — AFTER all STATE.md writes are complete and BEFORE the commit is staged in Phase 4.1. This ordering ensures a clean handover: the lock file is absent from the working tree when the commit is assembled, so it is not accidentally staged.
|
|
751
|
+
The lock is released here — AFTER all STATE.md writes are complete and BEFORE the commit is staged in Phase 4.1. This ordering ensures a clean handover when the current raw owner releases it: the lock file is absent from the working tree when the commit is assembled, so it is not accidentally staged.
|
|
750
752
|
|
|
751
753
|
## Phase 4: Commit & Push
|
|
752
754
|
|
|
@@ -780,11 +782,53 @@ git push origin HEAD
|
|
|
780
782
|
```
|
|
781
783
|
|
|
782
784
|
### 4.4 GitHub Mirror (if configured in Session Config)
|
|
785
|
+
|
|
786
|
+
Three states, three DISTINGUISHABLE outcomes. The predecessor of this block
|
|
787
|
+
(`git remote get-url github 2>/dev/null && git push github HEAD 2>/dev/null || echo "GitHub mirror: not configured"`)
|
|
788
|
+
collapsed a **failed push** into `GitHub mirror: not configured` and exited 0 — git's real
|
|
789
|
+
error went to `/dev/null`, so a broken mirror was indistinguishable from an unconfigured one
|
|
790
|
+
(`.claude/rules/bash-harness-pitfalls.md` — "Silence is not success"). That matters more once
|
|
791
|
+
anything is wired to the mirror (e.g. a Vercel Git deploy): a silently-failing push means the
|
|
792
|
+
downstream artifact never updates and nobody is told.
|
|
793
|
+
|
|
794
|
+
Run it verbatim — `tests/skills/session-end/github-mirror-push.test.mjs` extracts the block
|
|
795
|
+
between the markers and executes it, so no second copy of this command may exist.
|
|
796
|
+
|
|
783
797
|
```bash
|
|
784
|
-
#
|
|
785
|
-
|
|
798
|
+
# --- github-mirror-push:begin ---
|
|
799
|
+
# Only attempt if 'mirror: github' is in Session Config.
|
|
800
|
+
# State 0: not a git repository at all → loud WARN, exit 1. This state was MISSED
|
|
801
|
+
# in the first version and is the reason it is listed first now: outside
|
|
802
|
+
# a repo, `git remote get-url` fails with "fatal: not a git repository",
|
|
803
|
+
# which is indistinguishable from "no such remote" by exit code alone.
|
|
804
|
+
# The block then announced "no 'github' remote configured — skipping
|
|
805
|
+
# (not an error)" and exited 0 — fail-open, in the very fix written to
|
|
806
|
+
# close a fail-open. Found by an adversarial reviewer, not by the author.
|
|
807
|
+
# State 1: no 'github' remote → informational, exit 0 (legitimate for consumer repos)
|
|
808
|
+
# State 2: push succeeded → confirmation WITH the pushed SHA, exit 0
|
|
809
|
+
# State 3: push FAILED → loud WARN on stderr WITH git's real output, exit 1
|
|
810
|
+
if ! git_dir=$(git rev-parse --git-dir 2>&1); then
|
|
811
|
+
echo "WARN GitHub mirror: not a git repository — cannot mirror anything." >&2
|
|
812
|
+
echo " git said: ${git_dir}" >&2
|
|
813
|
+
exit 1
|
|
814
|
+
elif ! mirror_url=$(git remote get-url github 2>&1); then
|
|
815
|
+
echo "GitHub mirror: no 'github' remote configured — skipping (not an error)."
|
|
816
|
+
echo " git said: ${mirror_url}" >&2
|
|
817
|
+
elif push_out=$(git push github HEAD 2>&1); then
|
|
818
|
+
echo "GitHub mirror: pushed $(git rev-parse HEAD) -> ${mirror_url}"
|
|
819
|
+
else
|
|
820
|
+
echo "WARN GitHub mirror PUSH FAILED: $(git rev-parse HEAD) is NOT on ${mirror_url}" >&2
|
|
821
|
+
echo "${push_out}" >&2
|
|
822
|
+
echo "WARN Mirror is stale — anything wired to it (site deploy) will not update." >&2
|
|
823
|
+
exit 1
|
|
824
|
+
fi
|
|
825
|
+
# --- github-mirror-push:end ---
|
|
786
826
|
```
|
|
787
827
|
|
|
828
|
+
State 3 exits non-zero on purpose: it is the only machine-readable signal that the mirror is
|
|
829
|
+
behind. Report it to the operator in the session summary; do not retry silently and do not
|
|
830
|
+
swallow it with `|| true`.
|
|
831
|
+
|
|
788
832
|
## Phase 4a: Auto-Promoted Worktree Cleanup (#575 P3.2)
|
|
789
833
|
|
|
790
834
|
> Skip if `persistence: false` in Session Config. Skip silently if the current worktree is NOT an Auto-promoted sibling (the common case).
|
|
@@ -848,15 +892,24 @@ if (!promoted) {
|
|
|
848
892
|
When the worktree is dirty (uncommitted, untracked, OR unpushed), render this AUQ via the coordinator's `AskUserQuestion` tool. The AUQ is coordinator-only — per `.claude/rules/ask-via-tool.md` AUQ-004, dispatched agents cannot call AUQ. Calling `git worktree remove --force` without explicit operator confirmation would violate PSA-003 (destructive action safeguards) — the dirty state may contain another session's work-in-progress or unmerged commits.
|
|
849
893
|
|
|
850
894
|
```js
|
|
895
|
+
// What is actually at stake, shown beside the options via `preview` (AUQ-006):
|
|
896
|
+
// the operator must see WHICH changes he would lose before he authorises the delete.
|
|
897
|
+
// Capped at 10 lines so the preview never outgrows the option list next to it.
|
|
898
|
+
const dirtyDetail = execFileSync('git', ['-C', promoted.wtPath, 'status', '--short', '--branch'], { encoding: 'utf8' })
|
|
899
|
+
.trim()
|
|
900
|
+
.split('\n')
|
|
901
|
+
.slice(0, 10)
|
|
902
|
+
.join('\n');
|
|
903
|
+
|
|
851
904
|
AskUserQuestion({
|
|
852
905
|
questions: [{
|
|
853
906
|
question: `Auto-promoted worktree at ${promoted.wtPath} has uncommitted/untracked/unpushed changes. How should I proceed?`,
|
|
854
|
-
header: "Worktree
|
|
907
|
+
header: "Worktree",
|
|
855
908
|
multiSelect: false,
|
|
856
909
|
options: [
|
|
857
|
-
{ label: "Behalten (Recommended)", description: "
|
|
858
|
-
{ label: "Löschen", description: "I confirm the changes are handled or expendable. Run 'git worktree remove --force' on the worktree." },
|
|
859
|
-
{ label: "Manuell", description: "Exit /close. I will inspect the worktree before re-running /close." },
|
|
910
|
+
{ label: "Behalten (Recommended)", description: "Keeps the worktree exactly as it is — nothing is deleted, and you can still remove it by hand later.", preview: `Stays on disk:\n${dirtyDetail}` },
|
|
911
|
+
{ label: "Löschen", description: "I confirm the changes are handled or expendable. Run 'git worktree remove --force' on the worktree.", preview: `Deleted with the worktree:\n${dirtyDetail}` },
|
|
912
|
+
{ label: "Manuell", description: "Exit /close. I will inspect the worktree before re-running /close.", preview: `You would inspect this first:\n${dirtyDetail}` },
|
|
860
913
|
],
|
|
861
914
|
}],
|
|
862
915
|
});
|
|
@@ -865,8 +918,8 @@ AskUserQuestion({
|
|
|
865
918
|
**Codex CLI / Cursor IDE fallback** (numbered Markdown list):
|
|
866
919
|
|
|
867
920
|
```
|
|
868
|
-
Worktree cleanup options:
|
|
869
|
-
1. **Behalten (Recommended)** —
|
|
921
|
+
Worktree cleanup options (the changes at stake are the `git status --short --branch` lines printed above):
|
|
922
|
+
1. **Behalten (Recommended)** — Keeps the worktree exactly as it is; nothing is deleted, and you can still remove it by hand later.
|
|
870
923
|
2. **Löschen** — I confirm the changes are handled or expendable. Run 'git worktree remove --force'.
|
|
871
924
|
3. **Manuell** — Exit /close. I will inspect the worktree before re-running /close.
|
|
872
925
|
Reply with the number of your choice.
|
|
@@ -984,16 +1037,38 @@ if (sweep) {
|
|
|
984
1037
|
**Ordering (load-bearing):** run this as the LAST issue-creating action of Phase 5 — after step 3, after "Discovery Issue Creation", after step 4 — and re-read the counter file at that moment. Those steps can themselves push new entries into `overflow[]`; draining early would leave them unfiled.
|
|
985
1038
|
|
|
986
1039
|
```js
|
|
987
|
-
import {
|
|
988
|
-
|
|
1040
|
+
import { readFileSync } from 'node:fs';
|
|
1041
|
+
import {
|
|
1042
|
+
readBudgetState,
|
|
1043
|
+
budgetStatePath,
|
|
1044
|
+
resolveIssueBudgetSessionId,
|
|
1045
|
+
} from '${PLUGIN_ROOT}/scripts/lib/issue-budget.mjs';
|
|
1046
|
+
|
|
1047
|
+
// `sessionId` is the physical raw lock/registry identity from session-start.
|
|
1048
|
+
const rawSessionId = sessionId;
|
|
1049
|
+
let currentSession = null;
|
|
1050
|
+
try {
|
|
1051
|
+
currentSession = JSON.parse(
|
|
1052
|
+
readFileSync(`${repoRoot}/.orchestrator/current-session.json`, 'utf8'),
|
|
1053
|
+
);
|
|
1054
|
+
} catch { /* no verified semantic accounting bridge */ }
|
|
1055
|
+
const accountingSessionId = resolveIssueBudgetSessionId(rawSessionId, currentSession);
|
|
1056
|
+
const state = readBudgetState(repoRoot, accountingSessionId);
|
|
1057
|
+
// { sessionId, count, exempt, overflow: [...] }
|
|
989
1058
|
```
|
|
990
1059
|
|
|
1060
|
+
`accountingSessionId` may be semantic only after
|
|
1061
|
+
`currentSession.session_id === rawSessionId`; this is budget accounting, not
|
|
1062
|
+
lock/registry ownership. When that proof is absent it remains the raw id.
|
|
1063
|
+
A host rotation that changes both raw and semantic values has no guaranteed
|
|
1064
|
+
budget continuity.
|
|
1065
|
+
|
|
991
1066
|
- **`issue-budget.overflow: collect-issue` (default)** — create exactly ONE issue:
|
|
992
|
-
- Title: `[Backlog-Sammel] <
|
|
1067
|
+
- Title: `[Backlog-Sammel] <accountingSessionId>, <N> zurückgestellte Punkte`
|
|
993
1068
|
- Labels: `type::backlog`, `priority::low`
|
|
994
1069
|
- Body: a Markdown checklist with one `- [ ]` line per `overflow[]` entry (`title` when present, otherwise the truncated `command`, plus its `at` timestamp).
|
|
995
1070
|
- This collector issue is itself EXEMPT from the cap (`[Backlog-Sammel]` is in the exemption list in `scripts/lib/issue-budget.mjs`), so it always lands even at count == max.
|
|
996
|
-
- **`issue-budget.overflow: vault-note`** — create NO issue. Write one Markdown file `vault/00-inbox/<
|
|
1071
|
+
- **`issue-budget.overflow: vault-note`** — create NO issue. Write one Markdown file `vault/00-inbox/<accountingSessionId>-backlog-sammel.md` (path relative to `vault-integration.vault-dir`) with valid vault frontmatter and the same checklist body.
|
|
997
1072
|
- After the artefact exists, reset `overflow` to `[]` in the counter file and record the collector issue ID / note path in the Phase 6 Final Report under `### Zurückgestellt (issue-budget)`.
|
|
998
1073
|
- **Never exempt-by-accident:** the cap never applied to `priority::critical`, the carryover class (`[Carryover]`, SPIRAL/FAILED, `type::carryover`), or `broken-window` closure issues, so nothing on the Phase 1.65 carry-list can ever appear in `overflow[]`. The promises at Phase 1.8 ("SPIRAL / FAILED agent carryover … non-deselectable") and the Critical Rule "ALWAYS create issues for unfinished PLANNED work" stay intact by construction.
|
|
999
1074
|
- Fail-open: a missing or malformed counter file means "no overflow" — log a WARN and continue the close.
|
|
@@ -1092,7 +1167,7 @@ Present to the user:
|
|
|
1092
1167
|
| `phase-3-7a-recommendations.md` § 3.7b | Phase 3.7b full procedural body — `withDurableCommit` invocation for `sessions.jsonl` + `STATE.md` (#490 AC2), `enabled:false` local no-op, autopilot.jsonl exclusion note |
|
|
1093
1168
|
| (inline) Phase 3.7c | Vault Board → Closed (#674) — `mirrorBoard({ explicitStatus: 'closed' })` transitions this repo's board row to `closed`; gated on `vault-integration.enabled`, generator-marked + idempotent, non-blocking, ordered after 3.7b and before 3.7d/3.4/3.8 |
|
|
1094
1169
|
| (inline) Phase 3.7d | Session-Eval (opt-in — #803) — `node scripts/eval-session.mjs --json` scores the just-closed session; gated on `eval.enabled` + `eval.mode != off` (parsed by `scripts/lib/config/eval.mjs`), optional `eval-judge` dispatch + `writeEvalReport`, advisory/never-blocks-close, ordered after 3.7 (record must exist) and before 3.4/Phase 4 (record committed with the session). Full flow in `skills/eval/SKILL.md` |
|
|
1095
|
-
| (inline) Phase 3.8 | Session Lock Release — `release()`
|
|
1170
|
+
| (inline) Phase 3.8 | Session Lock Release — `release()` uses the physical raw `session_id`; raw mismatch/absent is non-fatal but never repaired with semantic labels or proof (live ambiguity remains for TTL/Reaper); fs-errors are non-fatal; runs after STATE.md writes and before Phase 4 commit staging |
|
|
1096
1171
|
|
|
1097
1172
|
## Anti-Patterns
|
|
1098
1173
|
|
|
@@ -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)
|
|
@@ -81,7 +81,25 @@ The proposals queue is populated mid-session by wave-executor agents calling `no
|
|
|
81
81
|
}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Then iterate `batches` and emit one `AskUserQuestion` per batch
|
|
84
|
+
Then iterate `batches` and emit one `AskUserQuestion` per batch. The verbatim template is `agents/memory-proposal-collector.md` § AUQ Question Template — keep the two in step:
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
AskUserQuestion({
|
|
88
|
+
questions: [{
|
|
89
|
+
header: "Memory",
|
|
90
|
+
question: "Batch <N> of <M> — which of these learnings should be stored permanently?",
|
|
91
|
+
options: [
|
|
92
|
+
// one entry per proposal in this batch (max 4)
|
|
93
|
+
// label + description formats are LOCKED by D3 — see that file, do not restate them here
|
|
94
|
+
{ label: "[type ] | subject(40) | conf=X.XX", description: "evidence: <first 60 chars of insight>" },
|
|
95
|
+
...
|
|
96
|
+
],
|
|
97
|
+
multiSelect: true
|
|
98
|
+
}]
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The batch counter moved out of `header` and into the question because `header` is cut off after 12 characters — `Memory — Confirm Proposals (Batch N of M)` reached the operator as `Memory — Con`.
|
|
85
103
|
|
|
86
104
|
5. After all batches answered, partition the queue into `approved` (any option selected across all batches) and `rejected` (all unselected).
|
|
87
105
|
|
|
@@ -337,7 +355,24 @@ After the auto-dialectic nudge decision is made (Phase 3.6.7), and when the reco
|
|
|
337
355
|
}
|
|
338
356
|
```
|
|
339
357
|
|
|
340
|
-
Iterate `batches` and emit one `AskUserQuestion` per batch
|
|
358
|
+
Iterate `batches` and emit one `AskUserQuestion` per batch:
|
|
359
|
+
|
|
360
|
+
```javascript
|
|
361
|
+
AskUserQuestion({
|
|
362
|
+
questions: [{
|
|
363
|
+
header: "Regeln",
|
|
364
|
+
question: "Batch <N> of <M> — which rule proposals should be written into .claude/rules/?",
|
|
365
|
+
options: [
|
|
366
|
+
// one entry per proposal in this batch (max 4)
|
|
367
|
+
{ label: "<slug-40>", description: "Confidence <confidence>. First 80 chars of the rendered rule text: <…>" },
|
|
368
|
+
...
|
|
369
|
+
],
|
|
370
|
+
multiSelect: true
|
|
371
|
+
}]
|
|
372
|
+
})
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
The batch counter moved out of `header` and into the question because `header` is cut off after 12 characters — `Reconciliation — Confirm Rule Proposals (Batch N of M)` reached the operator as `Reconciliati`. The rendered `content` shown in the description is the rule prose that will land on disk.
|
|
341
376
|
|
|
342
377
|
6. After all batches are answered, partition proposals into `approved` (any option selected across all batches) and `rejected` (all unselected). Proposals the operator rejected join the engine's `rejected` array for archival.
|
|
343
378
|
|
|
@@ -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
|
|
|
@@ -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
|
|