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
|
@@ -22,15 +22,18 @@ Fires when an `exclusive`-class session (`bootstrap`, `housekeeping`, `memory-cl
|
|
|
22
22
|
### Claude Code (AskUserQuestion)
|
|
23
23
|
|
|
24
24
|
```js
|
|
25
|
+
// Unpacked once so the question reads as one sentence instead of five key=value pairs.
|
|
26
|
+
const { mode, host, pid, worktreePath } = blockingSession;
|
|
27
|
+
|
|
25
28
|
AskUserQuestion({
|
|
26
29
|
questions: [{
|
|
27
|
-
question: `
|
|
28
|
-
header: "
|
|
30
|
+
question: `A ${mode} session (process ${pid} on ${host}) started ${ageHours}h ago in ${worktreePath}. What now?`,
|
|
31
|
+
header: "Repo belegt",
|
|
29
32
|
multiSelect: false,
|
|
30
33
|
options: [
|
|
31
|
-
{ label: "Warten (Recommended)", description: "
|
|
32
|
-
{ label: "Andere Session beenden", description: "
|
|
33
|
-
{ label: "Abbrechen", description: "Exit
|
|
34
|
+
{ label: "Warten (Recommended)", description: "Nothing else can start here until that session closes. This command does not retry — run it again afterwards." },
|
|
35
|
+
{ label: "Andere Session beenden", description: "You close it yourself, then run this command again. Nothing here stops the other session for you." },
|
|
36
|
+
{ label: "Abbrechen", description: "Exit now. Nothing is written: no STATE.md, no lock." },
|
|
34
37
|
],
|
|
35
38
|
}],
|
|
36
39
|
});
|
|
@@ -39,19 +42,17 @@ AskUserQuestion({
|
|
|
39
42
|
### Codex CLI / Cursor IDE / Pi fallback (numbered Markdown list)
|
|
40
43
|
|
|
41
44
|
```
|
|
42
|
-
|
|
43
|
-
Mode: <blockingSession.mode>
|
|
44
|
-
Started: <ageHours>h ago (host=<host>, pid=<pid>)
|
|
45
|
-
Worktree: <blockingSession.worktreePath>
|
|
46
|
-
This blocks all other modes.
|
|
45
|
+
A <mode> session (process <pid> on <host>) started <ageHours>h ago in <worktreePath>. What now?
|
|
47
46
|
|
|
48
|
-
1. Warten (Recommended) —
|
|
49
|
-
2. Andere Session beenden —
|
|
50
|
-
3. Abbrechen — exit
|
|
47
|
+
1. Warten (Recommended) — nothing else can start here until that session closes; this command does not retry, so run it again afterwards.
|
|
48
|
+
2. Andere Session beenden — you close it yourself, then run this command again. Nothing here stops the other session for you.
|
|
49
|
+
3. Abbrechen — exit now. Nothing is written: no STATE.md, no lock.
|
|
51
50
|
|
|
52
51
|
Reply with the number of your choice.
|
|
53
52
|
```
|
|
54
53
|
|
|
54
|
+
The four slots are `blockingSession.mode`, `blockingSession.host`, `blockingSession.pid` and `blockingSession.worktreePath`; `<ageHours>` is the age of that session in hours.
|
|
55
|
+
|
|
55
56
|
### Outcome handling
|
|
56
57
|
|
|
57
58
|
- **Warten** → exit Phase-0 cleanly with stderr note `parallel-aware: waiting on exclusive session_id=<id>`. No retry loop.
|
|
@@ -65,34 +66,39 @@ Fires when the caller is `parallel-ok`-class AND another `parallel-ok` session i
|
|
|
65
66
|
### Claude Code (AskUserQuestion)
|
|
66
67
|
|
|
67
68
|
```js
|
|
69
|
+
// Unpacked once so the question reads as one sentence instead of three key=value pairs.
|
|
70
|
+
const { mode, pid } = parallelPeer;
|
|
71
|
+
|
|
68
72
|
AskUserQuestion({
|
|
69
73
|
questions: [{
|
|
70
|
-
question: `A
|
|
71
|
-
header: "
|
|
74
|
+
question: `A ${mode} session (process ${pid}) started ${ageHours}h ago in this same folder. Run separately or alongside?`,
|
|
75
|
+
header: "Wo starten?",
|
|
72
76
|
multiSelect: false,
|
|
73
77
|
options: [
|
|
74
|
-
{ label: "Worktree anlegen + starten (Recommended)", description: "
|
|
75
|
-
{ label: "Manuell — in-place daneben", description: "
|
|
76
|
-
{ label: "Abbrechen", description: "Exit
|
|
78
|
+
{ label: "Worktree anlegen + starten (Recommended)", description: "Creates a second working folder beside this one and starts there — isolates your edits, so nothing collides." },
|
|
79
|
+
{ label: "Manuell — in-place daneben", description: "Both sessions write in this same folder — conflicts are likely and you resolve them yourself. A Deviation is logged." },
|
|
80
|
+
{ label: "Abbrechen", description: "Exit now. Nothing is written: no STATE.md, no lock." },
|
|
77
81
|
],
|
|
78
82
|
}],
|
|
79
83
|
});
|
|
80
84
|
```
|
|
81
85
|
|
|
86
|
+
The second working folder is a git worktree at `<basePath>/<repo-name>-<sessionId>/`; `enterWorktree()` from `scripts/lib/autopilot/worktree-pipeline.mjs` creates it (see Outcome handling below). Running in-place puts PSA-001/PSA-002/PSA-004 discipline on the operator.
|
|
87
|
+
|
|
82
88
|
### Codex CLI / Cursor IDE / Pi fallback (numbered Markdown list)
|
|
83
89
|
|
|
84
90
|
```
|
|
85
|
-
|
|
86
|
-
Peer mode: <parallelPeer.mode>
|
|
87
|
-
Started: <ageHours>h ago (pid=<pid>)
|
|
91
|
+
A <mode> session (process <pid>) started <ageHours>h ago in this same folder. Run separately or alongside?
|
|
88
92
|
|
|
89
|
-
1. Worktree anlegen + starten (Recommended) —
|
|
90
|
-
2. Manuell — in-place daneben —
|
|
91
|
-
3. Abbrechen — exit
|
|
93
|
+
1. Worktree anlegen + starten (Recommended) — creates a second working folder beside this one and starts there; isolates your edits, so nothing collides.
|
|
94
|
+
2. Manuell — in-place daneben — both sessions write in this folder; conflicts are likely and you resolve them. A Deviation is logged.
|
|
95
|
+
3. Abbrechen — exit now. Nothing is written: no STATE.md, no lock.
|
|
92
96
|
|
|
93
97
|
Reply with the number of your choice.
|
|
94
98
|
```
|
|
95
99
|
|
|
100
|
+
The two slots are `parallelPeer.mode` and `parallelPeer.pid`; `<ageHours>` is the age of that session in hours.
|
|
101
|
+
|
|
96
102
|
### Outcome handling
|
|
97
103
|
|
|
98
104
|
- **Worktree anlegen + starten** → invoke `enterWorktree({ basePath, sessionId, branch, repoRoot })` from `scripts/lib/autopilot/worktree-pipeline.mjs`. The helper creates a sibling worktree at `<basePath>/<repo-name>-<sessionId>/`, runs idempotency + boundary checks, and logs a WARN line to stderr on fresh creation. Once the worktree exists, exit the current preamble flow — the new worktree's own session-start runs from scratch (Phase 1 onwards). On failure (`WorktreeBoundaryError` or `git worktree add` non-zero exit), emit a stderr warning `parallel-aware: enterWorktree failed: <error>; falling back to Manuell` and proceed via the Manuell path.
|
|
@@ -29,6 +29,33 @@ If an `exclusive`-class session is also active, the Exclusive-Conflict AUQ takes
|
|
|
29
29
|
**Always-OK class** (`discovery`, `evolve`, `plan`, `repo-audit`, `portfolio`):
|
|
30
30
|
The preamble passes through with zero AUQ regardless of other active sessions. Read-only modes never conflict.
|
|
31
31
|
|
|
32
|
+
## Identity Binding for `findPeers` (#1085)
|
|
33
|
+
|
|
34
|
+
`mySessionId` / `callerSessionId` is a **hint for the caller's original
|
|
35
|
+
surface**, not a license to turn an attribution label into ownership. A native
|
|
36
|
+
raw id self-excludes on the discovered lock/registry surface directly. Given a
|
|
37
|
+
semantic hint, `findPeers` may translate it to a concrete local raw id only when
|
|
38
|
+
both proofs hold: `current-session.json` has the same semantic label **and** its
|
|
39
|
+
raw `session_id` exactly equals the readable local lock's raw `session_id`.
|
|
40
|
+
|
|
41
|
+
On a missing, malformed, or mismatched binding, `findPeers` must map nothing and
|
|
42
|
+
leave the discovered lock visible. The STATE.md surface always receives the
|
|
43
|
+
original hint and therefore compares STATE.md `session` as the attribution label
|
|
44
|
+
it is; it is never rewritten to a raw id. This guarded translation is only
|
|
45
|
+
self-exclusion for discovery, not lock/registry ownership and not a continuity
|
|
46
|
+
bridge across a host rotation that changes both values.
|
|
47
|
+
|
|
48
|
+
**What the binding does not prove.** Both files it reads are repo-global, so the
|
|
49
|
+
check establishes that they are mutually CONSISTENT — not that they describe
|
|
50
|
+
*this* process. Semantic labels are routinely shared between simultaneously live
|
|
51
|
+
sessions, and when a foreign live session wrote both files last under a label
|
|
52
|
+
equal to this hint, its raw id is filtered out and the peer disappears from the
|
|
53
|
+
result. Measured 2026-08-21: with a null hint the foreign peer is returned, with
|
|
54
|
+
the colliding semantic hint `peers` is empty. Treat a quiet `findPeers` result as
|
|
55
|
+
weaker evidence than a git or filesystem signal, and prefer passing the native
|
|
56
|
+
raw id whenever the caller has one. Closing this needs a per-process ownership
|
|
57
|
+
proof; see #1091.
|
|
58
|
+
|
|
32
59
|
## Preamble Algorithm
|
|
33
60
|
|
|
34
61
|
Execute these steps in order. Any classification determines outcome.
|
|
@@ -152,8 +179,10 @@ findPeers(repoRoot, { mySessionId }) → peer = peers.find((p) => p.source === '
|
|
|
152
179
|
```js
|
|
153
180
|
import { findPeers } from '../../scripts/lib/peer-discovery.mjs';
|
|
154
181
|
|
|
155
|
-
// Inside Phase 1b, before writing STATE.md
|
|
156
|
-
|
|
182
|
+
// Inside Phase 1b, before writing STATE.md. Preserve the original
|
|
183
|
+
// attribution-label hint for the STATE.md surface; findPeers guards any
|
|
184
|
+
// semantic→raw translation for discovered peers internally.
|
|
185
|
+
const { peers } = await findPeers(repoRoot, { mySessionId: callerSessionHint });
|
|
157
186
|
const peer = peers.find((p) => p.source === 'state-md') ?? null;
|
|
158
187
|
// Phase 1.2.1 consumes only the 'state-md' subset (STATE.md surface only).
|
|
159
188
|
if (peer !== null) {
|
|
@@ -17,7 +17,7 @@ current-wave: <N>
|
|
|
17
17
|
total-waves: <N>
|
|
18
18
|
# Optional fields (schema-version 1, additive for backward-compat):
|
|
19
19
|
updated: <ISO 8601 UTC> # last write timestamp, touched by any writer
|
|
20
|
-
session: <session-
|
|
20
|
+
session: <session-label> # attribution/history label; normally semantic since #573, legacy UUID-v4 remains readable; never a lock/registry ownership key
|
|
21
21
|
session-start-ref: <sha> # git ref at session start
|
|
22
22
|
---
|
|
23
23
|
```
|
|
@@ -25,7 +25,7 @@ session-start-ref: <sha> # git ref at session start
|
|
|
25
25
|
### Required vs. optional fields
|
|
26
26
|
|
|
27
27
|
- `schema-version`, `session-type`, `branch`, `issues`, `started_at`, `status`, `current-wave`, `total-waves` — **required** in every session-owned STATE.md.
|
|
28
|
-
- `updated`, `session`, `session-start-ref` — **optional**. Added by #184. STATE.md files without these fields remain valid and should be treated as `updated: null` / `session: null`. Writers SHOULD populate these fields but readers MUST tolerate their absence.
|
|
28
|
+
- `updated`, `session`, `session-start-ref` — **optional**. Added by #184. STATE.md files without these fields remain valid and should be treated as `updated: null` / `session: null`. Writers SHOULD populate these fields but readers MUST tolerate their absence. `session` is an attribution/history label, normally `<branch>-<YYYY-MM-DD>-<mode>-<n>` since #573 (Epic #568 Parallel-Aware Sessions P2.2); pre-#573 files may contain a UUID-v4 — both formats are read via `parseSessionId()` from `scripts/lib/session-id.mjs` per PRD §3 P2 row 3 (backward-compat). Neither form grants lock or registry ownership.
|
|
29
29
|
|
|
30
30
|
The `session-type: none` + `status: idle` combination is used only for bootstrap-scaffolded placeholder files (no active session).
|
|
31
31
|
|
|
@@ -79,6 +79,32 @@ A log of unresolved, user-facing questions surfaced by wave agents during a sess
|
|
|
79
79
|
|
|
80
80
|
Helpers: `readOpenQuestions` (pure), `appendOpenQuestion` (pure), `markOpenQuestionAnswered` (pure), `appendOpenQuestionOnDisk` (lock-guarded write), `markOpenQuestionAnsweredOnDisk` (lock-guarded write) — all exported from `scripts/lib/state-md.mjs`.
|
|
81
81
|
|
|
82
|
+
## Session Identity and Lock Ownership (#1085)
|
|
83
|
+
|
|
84
|
+
This contract distinguishes a physical live-session key from labels that make a
|
|
85
|
+
session intelligible to people and history readers. It does not add an identity
|
|
86
|
+
layer.
|
|
87
|
+
|
|
88
|
+
- **`session_id` is the only live ownership key.** It is the native raw identity
|
|
89
|
+
supplied by the active harness, or a generated UUID when no trustworthy raw
|
|
90
|
+
identity is available. Lock acquisition, registry membership, self-exclusion,
|
|
91
|
+
proof checks, and lock release use this physical key.
|
|
92
|
+
- **`semantic_session_id` and STATE.md `session` are attribution/history
|
|
93
|
+
labels, never ownership.** They may describe the same work to a human, but
|
|
94
|
+
equality of either label cannot acquire, refresh, release, or reclaim a lock.
|
|
95
|
+
A legacy UUID in STATE.md remains readable only as historical data.
|
|
96
|
+
- **Never bridge a raw mismatch with a label or a proof.** If the current raw
|
|
97
|
+
id and a live lock's raw id differ, ownership is ambiguous. Leave the live
|
|
98
|
+
lock visible and let its TTL/Reaper lifecycle resolve it; do not substitute a
|
|
99
|
+
semantic match, STATE.md `session` match, or owner-proof match.
|
|
100
|
+
- **There is no `logical_session_id`.** A true cross-harness restart-continuity
|
|
101
|
+
contract requires a trusted native resume identifier and remains a follow-up.
|
|
102
|
+
In particular, a host rotation that changes both raw and semantic values has
|
|
103
|
+
no guaranteed continuity.
|
|
104
|
+
|
|
105
|
+
The peer-discovery and issue-budget procedures below apply these rules at their
|
|
106
|
+
narrow surfaces; neither creates a second ownership model.
|
|
107
|
+
|
|
82
108
|
## CCU-009 — Status = Index, Never History (#730/H6)
|
|
83
109
|
|
|
84
110
|
> Adopted from an external-repo fleet-mining finding (2026-07-02): narrative
|
|
@@ -174,8 +200,8 @@ The `.orchestrator/session.lock` file is written mechanically by `hooks/_lib/loc
|
|
|
174
200
|
|
|
175
201
|
```json
|
|
176
202
|
{
|
|
177
|
-
"session_id": "<
|
|
178
|
-
"semantic_session_id": "<
|
|
203
|
+
"session_id": "<native-raw-id OR generated-UUID>",
|
|
204
|
+
"semantic_session_id": "<attribution-label>",
|
|
179
205
|
"started_at": "<ISO-8601 UTC>",
|
|
180
206
|
"last_heartbeat": "<ISO-8601 UTC>",
|
|
181
207
|
"mode": "deep|feature|housekeeping|session|...",
|
|
@@ -189,8 +215,8 @@ The `.orchestrator/session.lock` file is written mechanically by `hooks/_lib/loc
|
|
|
189
215
|
|
|
190
216
|
| Field | Required since | Description |
|
|
191
217
|
|---|---|---|
|
|
192
|
-
| `session_id` | v1 | The
|
|
193
|
-
| `semantic_session_id` | v2 (Epic #583) |
|
|
218
|
+
| `session_id` | v1 | The physical live lock/registry ownership key: a native raw harness identity, or a generated UUID when no trustworthy raw identity exists. Never use a semantic label here. |
|
|
219
|
+
| `semantic_session_id` | v2 (Epic #583) | An attribution/history label, normally `<branch>-<YYYY-MM-DD>-<mode>-<n>`, surfaced alongside the raw key. It never establishes lock or registry ownership, including when it equals STATE.md `session`. |
|
|
194
220
|
| `started_at` | v1 | ISO-8601 timestamp when the lock was written. |
|
|
195
221
|
| `last_heartbeat` | v2 (Epic #583) | ISO-8601 timestamp updated by the `SessionStart` hook and by `PostToolBatch`/`Stop` hooks. **Basis for liveness determination** — replaces PID-liveness (see below). |
|
|
196
222
|
| `mode` | v1 | Session mode consulted by exclusivity-matrix. May be `"unknown"` in the provisional lock written by the hook before Session Config + AUQ have settled. |
|
|
@@ -219,3 +245,20 @@ Net: `pid` (field notes above) stays forensic-only; `last_heartbeat` freshness i
|
|
|
219
245
|
### Schema v1 → v2 backward-compat
|
|
220
246
|
|
|
221
247
|
Readers (e.g., `readLock()` in `session-lock.mjs`, `discoverActiveSessions()`) MUST tolerate absent `last_heartbeat` and `semantic_session_id` fields (v1 locks written before Epic #583). When `last_heartbeat` is absent, fall back to TTL-based expiry from `started_at`. When `semantic_session_id` is absent, treat as unknown.
|
|
248
|
+
|
|
249
|
+
#### Schema v1 Sunset — evaluated 2026-08-15, tolerance RETAINED (#595)
|
|
250
|
+
|
|
251
|
+
The 90-day sunset window from Epic #583 (target 2026-08-25) came due and the removal was evaluated against the live fleet. **Verdict: keep the three reader tolerances; the blocker is not v1 data, it is a second production copy of the rule.**
|
|
252
|
+
|
|
253
|
+
**Precondition — zero v1 artefacts on disk (measured 2026-08-15, this host):**
|
|
254
|
+
|
|
255
|
+
- `find ~/Projects ~/.claude ~/.config /tmp/claude-501 -name 'session.lock' -not -path '*/node_modules/*'` → **12 files, 12/12 carry a non-empty `last_heartbeat`** (0 v1).
|
|
256
|
+
- `~/.config/session-orchestrator/sessions/active/*.json` → **3 entries, 3/3 carry the `mode` key** (0 v1).
|
|
257
|
+
- The only co-installed older plugin build (`~/.claude/plugins/cache/session-orchestrator/session-orchestrator/3.13.0`) already writes `last_heartbeat` (`session-lock.mjs:195`) and `mode` (`session-registry.mjs:209`) — **no v1 writer remains on this host.**
|
|
258
|
+
|
|
259
|
+
**Why the branches stay anyway:**
|
|
260
|
+
|
|
261
|
+
1. **`parseLock()` / `isLockLive()` — the rule is duplicated.** `scripts/lib/harness-audit/categories/category4.mjs` `lockIsLive()` inlines the same `last_heartbeat ?? started_at` fallback, and `tests/lib/lock-ttl-parity.test.mjs` asserts the mirror and the SSOT return identical verdicts *for a v1 lock*. Dropping it in `session-lock.mjs` alone breaks that parity by construction. A measured removal attempt turned **18 tests red across 4 files** (`session-discovery` 9, `session-discovery-fallback` 6, `lock-ttl-parity` 1, `on-session-start` 2) — all outside the lock/registry module pair, all seeding v1-shaped fixtures.
|
|
262
|
+
2. **`_validEntry()` optional `mode` — removal is a net safety LOSS.** Rejecting a mode-less registry entry drops a **live peer** from `readRegistry()`, making it invisible to the exclusivity matrix. An absent `mode` already degrades to the `parallel-ok` bucket, so strictening buys no detection and costs peer visibility — the wrong direction under `.claude/rules/development.md` § Guard & Threshold Design.
|
|
263
|
+
|
|
264
|
+
**What a real sunset needs (co-change set, one atomic MR):** `scripts/lib/session-lock.mjs` + `scripts/lib/harness-audit/categories/category4.mjs` (the mirror) + fixture updates in `tests/lib/session-discovery.test.mjs`, `tests/lib/session-discovery-fallback.test.mjs`, `tests/lib/lock-ttl-parity.test.mjs`, `tests/hooks/on-session-start.test.mjs`. Deleting the *mirror* in favour of importing the SSOT is the durable fix — the duplication, not the v1 data, is what keeps this class alive. The registry item should be closed as won't-do per point 2.
|
|
@@ -79,7 +79,6 @@ AskUserQuestion({
|
|
|
79
79
|
question: "Leeres Repo erkannt. Basierend auf '<HEURISTIC_REASON>' empfehle ich **<RECOMMENDED_TIER>**. Passt das?",
|
|
80
80
|
header: "Bootstrap",
|
|
81
81
|
options: [
|
|
82
|
-
{ label: "<RECOMMENDED_TIER> (Empfohlen)", description: "<one-line description of what this tier scaffolds>" },
|
|
83
82
|
{ label: "fast", description: "Nur CLAUDE.md + .gitignore + README. Für Demos, Spikes, Playgrounds." },
|
|
84
83
|
{ label: "standard", description: "Fast + package.json/Manifest + TypeScript + Linting + Tests. Für MVPs und echte Produkte." },
|
|
85
84
|
{ label: "deep", description: "Standard + CI + CODEOWNERS + CHANGELOG. Für Production, Team, Langlebige Repos." },
|
|
@@ -90,6 +89,8 @@ AskUserQuestion({
|
|
|
90
89
|
})
|
|
91
90
|
```
|
|
92
91
|
|
|
92
|
+
Before rendering: append ` (Empfohlen)` to whichever of the three tier labels equals `<RECOMMENDED_TIER>`, and move that option to position 1. The recommended tier is one of the three — listing it a fourth time as its own option made five options, one more than `AskUserQuestion` accepts, and repeated the same choice twice.
|
|
93
|
+
|
|
93
94
|
If user selects "Abbrechen": stop. Report "Bootstrap abgebrochen. Kein Kommando wird ausgeführt." Do not continue.
|
|
94
95
|
|
|
95
96
|
Store confirmed tier as `CONFIRMED_TIER`.
|
|
@@ -48,12 +48,12 @@ Run a single AUQ to let the user characterize the problem in their own words:
|
|
|
48
48
|
```
|
|
49
49
|
AskUserQuestion({
|
|
50
50
|
questions: [{
|
|
51
|
-
question: "
|
|
52
|
-
header: "
|
|
51
|
+
question: "What do you want to brainstorm, and what is still unclear about it?",
|
|
52
|
+
header: "Frame",
|
|
53
53
|
options: [
|
|
54
|
-
{ label: "
|
|
55
|
-
{ label: "Scope / boundaries
|
|
56
|
-
{ label: "
|
|
54
|
+
{ label: "The user-facing surface", description: "You know what to build but not how it should work for the user." },
|
|
55
|
+
{ label: "Scope / boundaries", description: "You're not sure how much to build, or where one feature ends and another begins." },
|
|
56
|
+
{ label: "Several approaches — pick one", description: "You see 2-3 ways to solve this and aren't sure which is best." },
|
|
57
57
|
{ label: "Other / describe below", description: "Free-form: describe the ambiguity in the next prompt." }
|
|
58
58
|
],
|
|
59
59
|
multiSelect: false
|
|
@@ -98,9 +98,9 @@ Example round (adapt to the actual feature):
|
|
|
98
98
|
AskUserQuestion({
|
|
99
99
|
questions: [{
|
|
100
100
|
question: "Who is the primary user of this feature, and how do they trigger it?",
|
|
101
|
-
header: "User
|
|
101
|
+
header: "User surface",
|
|
102
102
|
options: [
|
|
103
|
-
{ label: "
|
|
103
|
+
{ label: "Signed-in user, UI action (Recommended)", description: "Pro: fits existing session model. Con: requires UI component work." },
|
|
104
104
|
{ label: "Automated trigger (webhook, cron, event)", description: "Pro: no manual user step. Con: harder to debug and test." },
|
|
105
105
|
{ label: "Admin-only operation", description: "Pro: simpler access control. Con: limits who can self-serve." },
|
|
106
106
|
{ label: "Other / describe below", description: "Describe the trigger mechanism." }
|
|
@@ -128,12 +128,12 @@ Once the dialogue has enough signal, synthesize 2-3 concrete implementation appr
|
|
|
128
128
|
```
|
|
129
129
|
AskUserQuestion({
|
|
130
130
|
questions: [{
|
|
131
|
-
question: "
|
|
132
|
-
header: "
|
|
131
|
+
question: "Which of these approaches fits best?",
|
|
132
|
+
header: "Approach",
|
|
133
133
|
options: [
|
|
134
|
-
{ label: "
|
|
135
|
-
{ label: "
|
|
136
|
-
{ label: "
|
|
134
|
+
{ label: "A — [short summary] (Recommended)", description: "Best balance of value, reversibility and scope, because [key pro]. Cost: [key con]. Complexity: low/medium/high." },
|
|
135
|
+
{ label: "B — [short summary]", description: "Trade-offs: [key pro]. Cost: [key con]. Complexity: low/medium/high." },
|
|
136
|
+
{ label: "C — [short summary]", description: "Trade-offs: [key pro]. Cost: [key con]. Complexity: low/medium/high." }
|
|
137
137
|
],
|
|
138
138
|
multiSelect: false
|
|
139
139
|
}]
|
|
@@ -231,13 +231,13 @@ Present the spec path and summary to the user. Then ask via AUQ:
|
|
|
231
231
|
```
|
|
232
232
|
AskUserQuestion({
|
|
233
233
|
questions: [{
|
|
234
|
-
question: "The
|
|
235
|
-
header: "
|
|
234
|
+
question: "The spec is at docs/specs/YYYY-MM-DD-<slug>-design.md. How do you want to proceed?",
|
|
235
|
+
header: "Hand-off",
|
|
236
236
|
options: [
|
|
237
|
-
{ label: "Proceed to /plan feature (Recommended)", description: "
|
|
238
|
-
{ label: "Proceed to /write-executable-plan", description: "
|
|
239
|
-
{ label: "Revise the spec", description: "
|
|
240
|
-
{ label: "Done for now", description: "
|
|
237
|
+
{ label: "Proceed to /plan feature (Recommended)", description: "Turns the spec into a PRD with acceptance criteria, then files the issues. Cost: one more question round." },
|
|
238
|
+
{ label: "Proceed to /write-executable-plan", description: "Skips the formal PRD and writes a step-by-step executable plan instead — tracked in #39 (a numbered ticket, not yet released)." },
|
|
239
|
+
{ label: "Revise the spec", description: "Describe what to change; the spec is updated and comes back here." },
|
|
240
|
+
{ label: "Done for now", description: "The spec stays on disk as a reference. Nothing else runs." }
|
|
241
241
|
],
|
|
242
242
|
multiSelect: false
|
|
243
243
|
}]
|
|
@@ -62,6 +62,18 @@ The active level is `efficiency.output-level` in `~/.config/session-orchestrator
|
|
|
62
62
|
- Shape: explain WHY each option is on the table, name the paths you already discarded and why, define unfamiliar terms on first use.
|
|
63
63
|
- Escalation: `expand <topic>` — see § Escalation above.
|
|
64
64
|
|
|
65
|
+
### Register — how a sentence reads
|
|
66
|
+
|
|
67
|
+
The budgets above set *how much* you say; the register sets *how*. It is
|
|
68
|
+
defined once, in `skills/session-start/soul.md` § "Register — how a sentence
|
|
69
|
+
reads", and binds here unchanged: the frame ("write for someone who knows this
|
|
70
|
+
project but has not seen what you just saw"), the plain-words test with its
|
|
71
|
+
five worked cases, and its precedence over § "Never traded for brevity" above.
|
|
72
|
+
Read it there. It is not repeated here on purpose — the § Output Levels intro
|
|
73
|
+
sentence already exists in four copies across the four souls with nothing
|
|
74
|
+
checking their parity, and a fifth copied rule would drift the same way. A
|
|
75
|
+
pointer cannot.
|
|
76
|
+
|
|
65
77
|
### Companion dials
|
|
66
78
|
|
|
67
79
|
Same file, same lookup, same fallback-to-default rule:
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: claude-md-drift-check
|
|
3
|
-
description:
|
|
3
|
+
description: >
|
|
4
|
+
Use when detecting drift between CLAUDE.md (or AGENTS.md, the Codex CLI alias) / _meta narrative and
|
|
5
|
+
live repository state. Ten checks: absolute-path resolution, 01-projects/ count claims, issue-reference
|
|
6
|
+
freshness, session-file existence, command-count sync, session-config-parity (mandatory template keys =
|
|
7
|
+
error, opt-in gaps = warning), vault-dir-parity (CLAUDE.md vs AGENTS.md), generated-rule-staleness
|
|
8
|
+
(WARN-only), rule-scoping (paths:/globs: frontmatter defects, dangling rule citations, zero-match
|
|
9
|
+
globs), and docs-parity (docs/components.md count-claims vs on-disk counts, template-vs-reference
|
|
10
|
+
config-key parity, stale .claude/metrics/ paths). Full per-check spec in the body table. Invoked as an
|
|
11
|
+
opt-in session-end phase; mirrors vault-sync's lean JSON+exit-code contract.
|
|
4
12
|
model: haiku
|
|
5
13
|
---
|
|
6
14
|
|
package/skills/debug/SKILL.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: debug
|
|
3
|
-
description:
|
|
3
|
+
description: >
|
|
4
|
+
Use when encountering any bug, test failure, build break, or unexpected behavior — runs a 4-phase
|
|
5
|
+
systematic debugging process before proposing any fix. Iron Law: NO FIXES WITHOUT ROOT CAUSE
|
|
6
|
+
INVESTIGATION FIRST. Produces a `.orchestrator/debug/` artifact the fixer agent must reference.
|
|
4
7
|
model: inherit
|
|
5
8
|
color: red
|
|
6
9
|
tools: Read, Grep, Glob, Bash, Write
|
|
@@ -104,13 +104,13 @@ When the gate above is satisfied, present exactly this AskUserQuestion (AUQ-003
|
|
|
104
104
|
```
|
|
105
105
|
AskUserQuestion({
|
|
106
106
|
questions: [{
|
|
107
|
-
question: "Scope `feature` was requested. How should this run handle
|
|
108
|
-
header: "
|
|
107
|
+
question: "Scope `feature` was requested. How should this run handle it?",
|
|
108
|
+
header: "Scope",
|
|
109
109
|
options: [
|
|
110
|
-
{ label: "Grounded scan (Recommended)", description: "
|
|
111
|
-
{ label: "Also judgment topics", description: "
|
|
112
|
-
{ label: "Route out", description: "No scan
|
|
113
|
-
{ label: "Skip", description: "
|
|
110
|
+
{ label: "Grounded scan (Recommended)", description: "Every finding is tied to a file and line and is verified before it can become an issue. Cost: two extra probes (intent-drift, stubbed-dead-feature)." },
|
|
111
|
+
{ label: "Also judgment topics", description: "Same scan, plus open product questions (opportunity framing, personas) kept as notes. They never become issues; after Phase 5 you pick where they go." },
|
|
112
|
+
{ label: "Route out", description: "No scan at all. You get a pointer to /brainstorm (product ideation) or /grill (assumption stress-test) instead." },
|
|
113
|
+
{ label: "Skip", description: "Drops `feature` (the probes for half-built and drifted features) from this run; the other scopes still run." }
|
|
114
114
|
],
|
|
115
115
|
multiSelect: false
|
|
116
116
|
}]
|
|
@@ -125,12 +125,12 @@ AskUserQuestion({
|
|
|
125
125
|
```
|
|
126
126
|
AskUserQuestion({
|
|
127
127
|
questions: [{
|
|
128
|
-
question: "
|
|
129
|
-
header: "
|
|
128
|
+
question: "Where should the collected judgment topics go?",
|
|
129
|
+
header: "Topics",
|
|
130
130
|
options: [
|
|
131
|
-
{ label: "Inline synthesis (Recommended)", description: "
|
|
132
|
-
{ label: "Route to /brainstorm", description: "
|
|
133
|
-
{ label: "Route to /plan feature", description: "
|
|
131
|
+
{ label: "Inline synthesis (Recommended)", description: "Sketches an outcome/persona pass into `### Judgment Topics (non-verified)` (a report section that never becomes issues). Cost: no second run." },
|
|
132
|
+
{ label: "Route to /brainstorm", description: "Hands the topics to /brainstorm as its opening context, for a full question-and-answer design dialogue." },
|
|
133
|
+
{ label: "Route to /plan feature", description: "Hands the topics to /plan feature as its opening context, for feature-PRD scoping." }
|
|
134
134
|
],
|
|
135
135
|
multiSelect: false
|
|
136
136
|
}]
|
|
@@ -370,14 +370,16 @@ For each Critical or High finding, use AskUserQuestion (on Codex CLI where AskUs
|
|
|
370
370
|
```
|
|
371
371
|
AskUserQuestion({
|
|
372
372
|
questions: [{
|
|
373
|
-
question: "<finding
|
|
374
|
-
header: "
|
|
373
|
+
question: "<severity> finding in <file_path> — what should happen with it?",
|
|
374
|
+
header: "Finding",
|
|
375
375
|
options: [
|
|
376
|
-
{ label: "Create issue (<severity>)", description: "
|
|
377
|
-
|
|
378
|
-
{ label: "
|
|
379
|
-
{ label: "Dismiss --
|
|
380
|
-
|
|
376
|
+
{ label: "Create issue (<severity>)", description: "Files it as priority::<severity>, so it is tracked outside this session. The code below is copied into the issue body.",
|
|
377
|
+
preview: "<finding title>\n\n<file_path>:<line_number>\n```\n<matched_text with +/-3 lines context>\n```\n\n<description>\n\nRecommended fix: <recommended_fix>" },
|
|
378
|
+
{ label: "Adjust priority", description: "Same issue, a priority you pick — this question then comes back with the new label." },
|
|
379
|
+
{ label: "Dismiss -- intentional", description: "The code is deliberate. Nothing is filed, and the finding stays only in this run's report." },
|
|
380
|
+
{ label: "Dismiss -- false positive", description: "The probe misread the code. Nothing is filed; worth reporting if the same probe misfires again." }
|
|
381
|
+
],
|
|
382
|
+
multiSelect: false
|
|
381
383
|
}]
|
|
382
384
|
})
|
|
383
385
|
```
|
|
@@ -391,13 +393,15 @@ Group remaining findings by category. For each category with medium/low findings
|
|
|
391
393
|
```
|
|
392
394
|
AskUserQuestion({
|
|
393
395
|
questions: [{
|
|
394
|
-
question: "[N] medium/low findings in [category]
|
|
395
|
-
header: "
|
|
396
|
+
question: "Create issues for all [N] medium/low findings in [category]?",
|
|
397
|
+
header: "Findings",
|
|
396
398
|
options: [
|
|
397
|
-
{ label: "Accept all (Recommended)", description: "
|
|
398
|
-
|
|
399
|
-
{ label: "
|
|
400
|
-
|
|
399
|
+
{ label: "Accept all (Recommended)", description: "Medium and low findings are cheap to file and cheap to close. Cost: [N] issues, roughly one second apart.",
|
|
400
|
+
preview: "1. [title] -- [file_path]:[line] ([severity])\n2. [title] -- [file_path]:[line] ([severity])\n..." },
|
|
401
|
+
{ label: "Review individually", description: "One question per finding, same options as the critical ones. Cost: [N] more prompts." },
|
|
402
|
+
{ label: "Dismiss all", description: "Nothing is filed for this category. The findings stay in this run's report only." }
|
|
403
|
+
],
|
|
404
|
+
multiSelect: false
|
|
401
405
|
}]
|
|
402
406
|
})
|
|
403
407
|
```
|
|
@@ -79,7 +79,7 @@ Used for a single finding from a single probe.
|
|
|
79
79
|
**CLI Example:**
|
|
80
80
|
```bash
|
|
81
81
|
# GitHub
|
|
82
|
-
gh issue create \
|
|
82
|
+
gh issue create -R <OWNER>/<REPO> \
|
|
83
83
|
--title "[Discovery] <finding_title>" \
|
|
84
84
|
--label "type:discovery,priority::<level>,area:<area>,status:ready" \
|
|
85
85
|
--body "$(cat <<'EOF'
|
|
@@ -88,7 +88,7 @@ EOF
|
|
|
88
88
|
)"
|
|
89
89
|
|
|
90
90
|
# GitLab
|
|
91
|
-
glab issue create \
|
|
91
|
+
glab issue create -R <OWNER>/<REPO> \
|
|
92
92
|
--title "[Discovery] <finding_title>" \
|
|
93
93
|
--label "type:discovery,priority::<level>,area:<area>,status:ready" \
|
|
94
94
|
--description "$(cat <<'EOF'
|
|
@@ -159,7 +159,7 @@ Used when multiple related findings from the same probe or category are grouped
|
|
|
159
159
|
**CLI Example:**
|
|
160
160
|
```bash
|
|
161
161
|
# GitHub
|
|
162
|
-
gh issue create \
|
|
162
|
+
gh issue create -R <OWNER>/<REPO> \
|
|
163
163
|
--title "[Discovery] <category> audit: <summary>" \
|
|
164
164
|
--label "type:discovery,priority::<level>,area:<area>,status:ready" \
|
|
165
165
|
--body "$(cat <<'EOF'
|
|
@@ -168,7 +168,7 @@ EOF
|
|
|
168
168
|
)"
|
|
169
169
|
|
|
170
170
|
# GitLab
|
|
171
|
-
glab issue create \
|
|
171
|
+
glab issue create -R <OWNER>/<REPO> \
|
|
172
172
|
--title "[Discovery] <category> audit: <summary>" \
|
|
173
173
|
--label "type:discovery,priority::<level>,area:<area>,status:ready" \
|
|
174
174
|
--description "$(cat <<'EOF'
|
|
@@ -58,9 +58,9 @@ Grep pattern: (TODO|FIXME|HACK|XXX|TEMP|WORKAROUND)[\s:()\-]
|
|
|
58
58
|
For each match, check whether a corresponding VCS issue exists:
|
|
59
59
|
```bash
|
|
60
60
|
# Search for issue referencing the annotation text
|
|
61
|
-
gh issue list --search "<annotation text>" --limit 5
|
|
61
|
+
gh issue list -R <OWNER>/<REPO> --search "<annotation text>" --limit 5
|
|
62
62
|
# or
|
|
63
|
-
glab issue list --search "<annotation text>" --per-page 5
|
|
63
|
+
glab issue list -R <OWNER>/<REPO> --search "<annotation text>" --per-page 5
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
Flag annotations with no corresponding issue.
|
|
@@ -113,11 +113,11 @@ Export Status: exported/public | internal
|
|
|
113
113
|
1. List open feature/enhancement issues via the VCS CLI (syntax reference: `skills/gitlab-ops/SKILL.md` § "Common CLI Commands" — do not duplicate CLI flags here beyond what's needed to name the call):
|
|
114
114
|
```bash
|
|
115
115
|
# GitLab
|
|
116
|
-
glab issue list --label "feature" --per-page 100
|
|
117
|
-
glab issue list --label "enhancement" --per-page 100
|
|
116
|
+
glab issue list -R <OWNER>/<REPO> --label "feature" --per-page 100
|
|
117
|
+
glab issue list -R <OWNER>/<REPO> --label "enhancement" --per-page 100
|
|
118
118
|
# GitHub
|
|
119
|
-
gh issue list --label "feature" --limit 100
|
|
120
|
-
gh issue list --label "enhancement" --limit 100
|
|
119
|
+
gh issue list -R <OWNER>/<REPO> --label "feature" --limit 100
|
|
120
|
+
gh issue list -R <OWNER>/<REPO> --label "enhancement" --limit 100
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
2. Extract theme keywords from each issue's title (and first paragraph of body where available): lowercase, strip stop-words, tokenize on non-alphanumeric boundaries. Group issues that share two or more significant keywords (e.g. "export", "csv", "invoice") into a candidate cluster.
|
|
@@ -125,10 +125,10 @@ gh issue list --label "enhancement" --limit 100
|
|
|
125
125
|
3. For each candidate cluster with 3 or more issues, check whether ANY member issue is already linked to an epic (`epic` label, a GitLab Epic relationship, or an in-body reference such as `part of #<epic-iid>` / `Epic: #<epic-iid>`):
|
|
126
126
|
```bash
|
|
127
127
|
# GitLab -- inspect labels + description for epic linkage
|
|
128
|
-
glab issue view <IID>
|
|
128
|
+
glab issue view -R <OWNER>/<REPO> <IID>
|
|
129
129
|
Grep pattern: (^Labels:.*\bepic\b|part of #|Epic:\s*#)
|
|
130
130
|
# GitHub -- same check against issue body/labels
|
|
131
|
-
gh issue view <NUMBER>
|
|
131
|
+
gh issue view -R <OWNER>/<REPO> <NUMBER>
|
|
132
132
|
Grep pattern: (^labels:.*\bepic\b|part of #|Epic:\s*#)
|
|
133
133
|
```
|
|
134
134
|
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
# GitLab: query recent pipeline status
|
|
13
|
-
glab pipeline list --per-page 10
|
|
13
|
+
glab pipeline list -R <OWNER>/<REPO> --per-page 10
|
|
14
14
|
|
|
15
15
|
# GitHub: query recent workflow runs
|
|
16
|
-
gh run list --limit 10
|
|
16
|
+
gh run list -R <OWNER>/<REPO> --limit 10
|
|
17
17
|
|
|
18
18
|
# Parse output for:
|
|
19
19
|
# - Repeated failures (same pipeline failing 3+ times in a row)
|
|
@@ -55,9 +55,9 @@ Grep pattern: <claimed_addition>
|
|
|
55
55
|
git show <commit_hash> -- <relevant_files>
|
|
56
56
|
|
|
57
57
|
# "Closes #N" -> verify acceptance criteria from issue #N are met
|
|
58
|
-
gh issue view <N> --json body -q '.body'
|
|
58
|
+
gh issue view -R <OWNER>/<REPO> <N> --json body -q '.body'
|
|
59
59
|
# or
|
|
60
|
-
glab issue view <N>
|
|
60
|
+
glab issue view -R <OWNER>/<REPO> <N>
|
|
61
61
|
|
|
62
62
|
# Step 3: Cross-reference claims against actual changes
|
|
63
63
|
git diff <commit_hash>~1..<commit_hash>
|
|
@@ -83,10 +83,10 @@ Evidence: <what was found or not found>
|
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
85
|
# GitLab: list open issues sorted by last update
|
|
86
|
-
glab issue list --per-page 100 | head -50
|
|
86
|
+
glab issue list -R <OWNER>/<REPO> --per-page 100 | head -50
|
|
87
87
|
|
|
88
88
|
# GitHub: list open issues sorted by last update
|
|
89
|
-
gh issue list --limit 100 --json number,title,labels,updatedAt,assignees --jq '.[] | select(.updatedAt < "<30_days_ago_iso>")'
|
|
89
|
+
gh issue list -R <OWNER>/<REPO> --limit 100 --json number,title,labels,updatedAt,assignees --jq '.[] | select(.updatedAt < "<30_days_ago_iso>")'
|
|
90
90
|
|
|
91
91
|
# Flag:
|
|
92
92
|
# - Issues with no activity in stale-issue-days (default: 30 days)
|
|
@@ -131,7 +131,7 @@ for issue in issues:
|
|
|
131
131
|
"
|
|
132
132
|
|
|
133
133
|
# GitHub: fetch issue bodies and parse cross-references
|
|
134
|
-
gh issue list --limit 100 --json number,body --jq '.[] | {number, body}' | python3 -c "
|
|
134
|
+
gh issue list -R <OWNER>/<REPO> --limit 100 --json number,body --jq '.[] | {number, body}' | python3 -c "
|
|
135
135
|
import json, sys, re
|
|
136
136
|
for line in sys.stdin:
|
|
137
137
|
issue = json.loads(line)
|