session-orchestrator 4.1.0 → 5.0.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/.agents/skills/session-plan/SKILL.md +1 -1
- package/.agents/skills/session-start/SKILL.md +1 -1
- package/.agents/skills/ux-grill/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +3 -2
- package/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/skills/session-plan/SKILL.md +1 -1
- package/.codex-plugin/skills/session-start/SKILL.md +1 -1
- package/.codex-plugin/skills/ux-grill/SKILL.md +21 -0
- package/.codex-plugin/skills/ux-grill/agents/openai.yaml +5 -0
- package/.cursor/commands/ux-grill.md +14 -0
- package/.cursor/skills/session-plan/SKILL.md +1 -1
- package/.cursor/skills/session-start/SKILL.md +1 -1
- package/.cursor/skills/ux-grill/SKILL.md +13 -0
- package/.cursor-plugin/plugin.json +1 -1
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +128 -1
- package/README.md +98 -86
- package/agents/dialectic-deriver.md +11 -0
- package/agents/ux-evaluator.md +1 -1
- package/commands/close.md +3 -3
- package/commands/go.md +2 -0
- package/commands/memory-cleanup.md +4 -3
- package/commands/persona-panel.md +1 -1
- package/commands/session.md +3 -2
- package/commands/ux-grill.md +51 -0
- package/docs/README.md +4 -4
- package/docs/USER-GUIDE.md +117 -50
- package/docs/agent-authoring.md +2 -2
- package/docs/baseline.md +55 -1
- package/docs/ci-setup.md +1 -1
- package/docs/codex-setup.md +9 -0
- package/docs/components.md +9 -9
- package/docs/cursor-setup.md +1 -0
- package/docs/events-schema.md +13 -6
- package/docs/github-mirror-protection.md +61 -20
- package/docs/instruction-delivery.md +1 -1
- package/docs/memory-proposal-flow.md +3 -3
- package/docs/migration-v4.md +2 -2
- package/docs/migration-v5.md +62 -0
- package/docs/owner-config-schema.md +74 -90
- package/docs/persona-panel.md +4 -4
- package/docs/pi-setup.md +1 -0
- package/docs/rule-authoring.md +13 -6
- package/docs/scope-collision-guard.md +16 -0
- package/docs/session-config-reference.md +55 -22
- package/docs/session-config-template.md +9 -5
- package/docs/vault-docs-architecture.md +4 -2
- package/hooks/_lib/hook-import-set.json +70 -3
- package/hooks/_lib/lock-bootstrap.mjs +84 -1
- package/hooks/_lib/vcs-create-matcher.mjs +401 -16
- package/hooks/enforce-scope.mjs +201 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +5 -0
- package/hooks/hooks.json +7 -2
- package/hooks/on-session-start.mjs +171 -49
- package/hooks/post-bash-issue-budget-refund.mjs +375 -0
- package/hooks/pre-auq-clarity.mjs +70 -18
- package/hooks/pre-bash-issue-budget.mjs +170 -26
- package/hooks/subagent-telemetry.mjs +106 -20
- package/package.json +5 -4
- package/pi/prompts/ux-grill.md +12 -0
- package/scripts/baseline-archetypes.mjs +28 -0
- package/scripts/ci/assert-vitest-green.mjs +4 -2
- package/scripts/dialectic-deriver.mjs +32 -8
- package/scripts/emit-session.mjs +72 -1
- package/scripts/lib/agent-status.mjs +441 -9
- package/scripts/lib/auq/schema.mjs +10 -3
- package/scripts/lib/auto-dialectic.mjs +0 -68
- package/scripts/lib/baseline-archetypes.mjs +439 -0
- package/scripts/lib/build-live-signals.mjs +5 -6
- package/scripts/lib/ci-status-banner.mjs +29 -6
- package/scripts/lib/claude-md-budget-lint.mjs +52 -2
- package/scripts/lib/config/issue-budget.mjs +68 -8
- package/scripts/lib/config/private-config-dir.mjs +3 -2
- package/scripts/lib/config/remote-hosts.mjs +2 -2
- package/scripts/lib/config-schema.mjs +79 -0
- package/scripts/lib/config.mjs +12 -1
- package/scripts/lib/eval/engine.mjs +7 -1
- package/scripts/lib/file-lock.mjs +151 -8
- package/scripts/lib/git-porcelain.mjs +113 -0
- package/scripts/lib/instruction-budget-guard.mjs +415 -47
- package/scripts/lib/io.mjs +29 -4
- package/scripts/lib/issue-budget-reconcile.mjs +392 -0
- package/scripts/lib/issue-budget.mjs +412 -9
- package/scripts/lib/learnings/evolve-telemetry.mjs +1 -2
- package/scripts/lib/learnings/sizing-subject.mjs +44 -0
- package/scripts/lib/locks/staging-fence-lock.mjs +19 -38
- package/scripts/lib/locks/state-md-lock.mjs +19 -41
- package/scripts/lib/maintenance-due-banner.mjs +450 -0
- package/scripts/lib/owner-config.example.yaml +29 -46
- package/scripts/lib/owner-yaml.mjs +14 -13
- package/scripts/lib/peer-cards/merger.mjs +143 -0
- package/scripts/lib/pre-dispatch-check.mjs +20 -14
- package/scripts/lib/project-hygiene.mjs +81 -30
- package/scripts/lib/quality-gate.mjs +27 -71
- package/scripts/lib/reconcile/engine.mjs +19 -1
- package/scripts/lib/reconcile/writer.mjs +278 -11
- package/scripts/lib/resource-probe/evaluate.mjs +19 -21
- package/scripts/lib/rules-sync.mjs +34 -4
- package/scripts/lib/scope-echo.mjs +346 -0
- package/scripts/lib/session-close-backfill.mjs +182 -40
- package/scripts/lib/session-end/phase-skip.mjs +85 -86
- package/scripts/lib/session-end/tail-runner.mjs +178 -0
- package/scripts/lib/session-lock.mjs +62 -2
- package/scripts/lib/session-record-repair.mjs +91 -0
- package/scripts/lib/session-schema/constants.mjs +6 -0
- package/scripts/lib/session-schema/filters.mjs +26 -1
- package/scripts/lib/session-schema/validator.mjs +20 -0
- package/scripts/lib/session-shape.mjs +558 -0
- package/scripts/lib/session-start-probes.mjs +429 -56
- package/scripts/lib/session-token-rollup.mjs +95 -10
- package/scripts/lib/state-md/frontmatter-mutators.mjs +22 -34
- package/scripts/lib/state-md.mjs +1 -0
- package/scripts/lib/subagents-schema.mjs +77 -9
- package/scripts/lib/telemetry/pricing.mjs +197 -0
- package/scripts/lib/telemetry/sync.mjs +50 -1
- package/scripts/lib/test-runner/artifact-paths.mjs +30 -5
- package/scripts/lib/test-runner/issue-reconcile.mjs +45 -8
- package/scripts/lib/tmux-layout/layouts.mjs +62 -4
- package/scripts/lib/ux-grill/collect.mjs +1163 -0
- package/scripts/lib/ux-grill/compare.mjs +285 -0
- package/scripts/lib/ux-grill/manifest.mjs +618 -0
- package/scripts/lib/ux-grill/measures.mjs +431 -0
- package/scripts/lib/ux-grill/paths.mjs +224 -0
- package/scripts/lib/ux-grill/pencil-coverage.mjs +284 -0
- package/scripts/lib/ux-grill/reconcile.mjs +344 -0
- package/scripts/lib/ux-grill/run-record.mjs +316 -0
- package/scripts/lib/ux-grill/schema.mjs +321 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +33 -10
- package/scripts/lib/validate/check-untracked-test-deps.mjs +33 -19
- package/scripts/lib/validate/check-unwired-features.mjs +56 -27
- package/scripts/lib/vault-mirror/process.mjs +2 -1
- package/scripts/lib/vault-status/board-lock.mjs +18 -0
- package/scripts/lib/vault-status/board-writer.mjs +8 -0
- package/scripts/lib/vault-status/narrative-mirror.mjs +4 -4
- package/scripts/lib/wave-resource-gate.mjs +23 -27
- package/scripts/lib/wave-sizing.mjs +10 -3
- package/scripts/materialize-wave-scope.mjs +68 -14
- package/scripts/mcp-server.sh +16 -1
- package/scripts/print-applicable-rules.mjs +7 -6
- package/scripts/print-learnings-index.mjs +3 -2
- package/scripts/release.mjs +7 -2
- package/scripts/session-shape.mjs +266 -0
- package/skills/_shared/config-reading.md +15 -9
- package/skills/_shared/private-capability-context.md +89 -0
- package/skills/bootstrap/SKILL.md +60 -209
- package/skills/bootstrap/_shared-template.md +99 -14
- package/skills/bootstrap/deep-template.md +36 -26
- package/skills/bootstrap/fast-template.md +44 -8
- package/skills/bootstrap/intensity-heuristic.md +10 -4
- package/skills/bootstrap/private-contract.md +119 -0
- package/skills/bootstrap/public-fallback.md +30 -18
- package/skills/bootstrap/references/bootstrap-ecosystem-health-flow.md +48 -0
- package/skills/bootstrap/references/bootstrap-refresh-lock-flow.md +37 -0
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +108 -0
- package/skills/bootstrap/references/bootstrap-rules-fetch-bridge.md +64 -0
- package/skills/bootstrap/standard-template.md +39 -24
- package/skills/claude-md-drift-check/SKILL.md +9 -2
- package/skills/claude-md-drift-check/checker.mjs +213 -21
- package/skills/discovery/SKILL.md +6 -173
- package/skills/discovery/probes/vault-staleness.mjs +35 -5
- package/skills/discovery/probes-docs.md +8 -4
- package/skills/discovery/probes-supply-chain.md +4 -2
- package/skills/discovery/probes-ui.md +8 -4
- package/skills/discovery/probes-vault.md +12 -4
- package/skills/discovery/references/discovery-interactive-triage.md +139 -0
- package/skills/discovery/references/discovery-triage-state.md +54 -0
- package/skills/docs-orchestrator/audience-mapping.md +1 -1
- package/skills/eval/rubric-v1.md +13 -0
- package/skills/evolve/SKILL.md +2 -458
- package/skills/evolve/references/evolve-analyze-mode.md +360 -0
- package/skills/evolve/references/evolve-dialectic-mode.md +139 -0
- package/skills/gitlab-ops/SKILL.md +3 -3
- package/skills/grill/SKILL.md +1 -1
- package/skills/memory-cleanup/SKILL.md +2 -2
- package/skills/plan/mode-new.md +9 -0
- package/skills/plan/mode-retro.md +4 -3
- package/skills/reconcile/SKILL.md +11 -1
- package/skills/session-end/SKILL.md +3 -2
- package/skills/session-end/drift-operations.md +20 -5
- package/skills/session-end/metrics-collection.md +1 -0
- package/skills/session-end/phase-3-2-docs-verification.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +27 -67
- package/skills/session-end/phase-3-7a-recommendations.md +2 -2
- package/skills/session-end/references/phase-2-quality-gate.md +3 -3
- package/skills/session-end/references/phase-3-documentation-updates.md +8 -6
- package/skills/session-end/references/phase-5-issue-cleanup.md +32 -1
- package/skills/session-end/session-metrics-write.md +33 -12
- package/skills/session-plan/SKILL.md +46 -180
- package/skills/session-plan/references/session-plan-task-classification.md +152 -0
- package/skills/session-plan/wave-template.md +8 -15
- package/skills/session-start/SKILL.md +41 -7
- package/skills/session-start/phase-2-5-docs-planning.md +1 -1
- package/skills/session-start/phase-8-5-express-path.md +12 -9
- package/skills/session-start/references/operations-contract.md +114 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +2 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +42 -24
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +3 -1
- package/skills/session-start/soul.md +2 -2
- package/skills/test-runner/SKILL.md +1 -1
- package/skills/test-runner/rubric-v1.md +2 -2
- package/skills/tmux-layout/SKILL.md +3 -1
- package/skills/ux-grill/SKILL.md +211 -0
- package/skills/ux-grill/rubric-v2.md +201 -0
- package/skills/ux-grill/soul.md +76 -0
- package/skills/wave-executor/SKILL.md +32 -127
- package/skills/wave-executor/circuit-breaker.md +3 -1
- package/skills/wave-executor/references/wave-executor-quality-gate.md +61 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +86 -0
- package/skills/wave-executor/references/wave-loop-dispatch.md +12 -2
- package/skills/wave-executor/references/wave-loop-review.md +19 -6
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +6 -2
- package/templates/_shared/ux-manifest.template.md +149 -0
- package/templates/nextjs-minimal/package.json +1 -1
- package/templates/node-minimal/package.json +1 -1
- package/scripts/lib/multi-provider-build/providers.mjs +0 -64
- package/scripts/lib/multi-provider-build/templating.mjs +0 -130
- package/scripts/lib/owner-config/coerce.mjs +0 -29
- package/scripts/lib/owner-config/constants.mjs +0 -21
- package/scripts/lib/owner-config/defaults.mjs +0 -50
- package/scripts/lib/owner-config/error.mjs +0 -19
- package/scripts/lib/owner-config/index.mjs +0 -13
- package/scripts/lib/owner-config/merge.mjs +0 -52
- package/scripts/lib/owner-config/validate.mjs +0 -259
- package/scripts/lib/owner-config-loader.mjs +0 -170
- package/scripts/lib/owner-config.mjs +0 -28
- package/scripts/lib/soul-resolve.mjs +0 -130
- package/scripts/lib/vault-mirror/render.mjs +0 -8
- package/templates/_shared/journey-manifest.md +0 -114
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* All functions are side-effect free. Callers are responsible for mkdir.
|
|
5
5
|
*
|
|
6
6
|
* Exports:
|
|
7
|
+
* RUN_ID_PATTERN → RegExp accepted run-id shape
|
|
8
|
+
* assertRunId(runId) → string the runId, or throws TypeError
|
|
7
9
|
* makeRunId() → string e.g., '12345-1715688000123'
|
|
8
10
|
* runDirPath(runId) → string
|
|
9
11
|
* findingsPath(runId) → string
|
|
@@ -19,6 +21,30 @@ import path from 'node:path';
|
|
|
19
21
|
const ROLLUP_REL = '.orchestrator/metrics/test-runs.jsonl';
|
|
20
22
|
const TEST_RUNS_REL = '.orchestrator/metrics/test-runs';
|
|
21
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Accepted shape of a run id — the single definition for test-runner AND
|
|
26
|
+
* ux-grill (`scripts/lib/ux-grill/paths.mjs` imports it; ux-grill → test-runner
|
|
27
|
+
* is the allowed dependency direction). A run id reaches `path.join` unescaped,
|
|
28
|
+
* so `/` would escape the run directory. `.` and `..` match this class too and
|
|
29
|
+
* are rejected separately by {@link assertRunId}.
|
|
30
|
+
* @type {RegExp}
|
|
31
|
+
*/
|
|
32
|
+
export const RUN_ID_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate a run id: a string matching {@link RUN_ID_PATTERN} that is not `.`
|
|
36
|
+
* or `..` (both resolve to the base dir or its parent — path traversal, #1330).
|
|
37
|
+
* @param {unknown} runId
|
|
38
|
+
* @returns {string} the same runId
|
|
39
|
+
* @throws {TypeError} if runId is not a string, does not match the pattern, or is `.`/`..`
|
|
40
|
+
*/
|
|
41
|
+
export function assertRunId(runId) {
|
|
42
|
+
if (typeof runId !== 'string' || !RUN_ID_PATTERN.test(runId) || runId === '.' || runId === '..') {
|
|
43
|
+
throw new TypeError(`runId must match ${RUN_ID_PATTERN} and not be "." or "..", got ${JSON.stringify(runId)}`);
|
|
44
|
+
}
|
|
45
|
+
return runId;
|
|
46
|
+
}
|
|
47
|
+
|
|
22
48
|
/**
|
|
23
49
|
* Generate a unique run-id from PID + millisecond timestamp.
|
|
24
50
|
* Format: `${process.pid}-${Date.now()}`
|
|
@@ -29,15 +55,14 @@ export function makeRunId() {
|
|
|
29
55
|
}
|
|
30
56
|
|
|
31
57
|
/**
|
|
32
|
-
* Construct the run-dir path for a given runId.
|
|
58
|
+
* Construct the run-dir path for a given runId. Every other per-run builder in
|
|
59
|
+
* this module routes through here, so this is the single path-traversal guard.
|
|
33
60
|
* @param {string} runId
|
|
34
61
|
* @returns {string}
|
|
62
|
+
* @throws {TypeError} on an invalid runId (see {@link assertRunId})
|
|
35
63
|
*/
|
|
36
64
|
export function runDirPath(runId) {
|
|
37
|
-
|
|
38
|
-
throw new TypeError('runDirPath: runId must be a non-empty string');
|
|
39
|
-
}
|
|
40
|
-
return path.join(TEST_RUNS_REL, runId);
|
|
65
|
+
return path.join(TEST_RUNS_REL, assertRunId(runId));
|
|
41
66
|
}
|
|
42
67
|
|
|
43
68
|
/**
|
|
@@ -148,6 +148,21 @@ function sanitizeRecommendation(text) {
|
|
|
148
148
|
return text.replace(/\*\*Fingerprint:\*\*/gi, '__Fingerprint__');
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Render untrusted text as the content of ONE inline Markdown code span:
|
|
153
|
+
* sentinel literals neutralised (sanitizeRecommendation), line breaks and NUL
|
|
154
|
+
* folded to a space, backticks turned into `'` — so a page-controlled locator
|
|
155
|
+
* can neither close its code span nor start a new body line.
|
|
156
|
+
*
|
|
157
|
+
* @param {unknown} text
|
|
158
|
+
* @returns {string}
|
|
159
|
+
*/
|
|
160
|
+
function codeSpanContent(text) {
|
|
161
|
+
return sanitizeRecommendation(String(text ?? ''))
|
|
162
|
+
.replace(/[\r\n\0]+/g, ' ')
|
|
163
|
+
.replace(/`/g, "'");
|
|
164
|
+
}
|
|
165
|
+
|
|
151
166
|
// ---------------------------------------------------------------------------
|
|
152
167
|
// Body-length validation (#389 SEC-IR-LOW-1)
|
|
153
168
|
// ---------------------------------------------------------------------------
|
|
@@ -182,25 +197,31 @@ function checkBodyLength(body) {
|
|
|
182
197
|
* Newlines within the body are intentional and safe — execFile passes
|
|
183
198
|
* --description as a single argv element, not through a shell.
|
|
184
199
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
200
|
+
* Every free-text field is sanitised before embedding, so the authoritative
|
|
201
|
+
* `**Fingerprint:** \`<fp>\`` sentinel cannot be spoofed: `recommendation`
|
|
202
|
+
* (#388) and `description` go through sanitizeRecommendation() — description
|
|
203
|
+
* is line 1, BEFORE the real sentinel, and carries verbatim page HTML (axe
|
|
204
|
+
* results), while the extractor takes the FIRST match. `locator` goes through
|
|
205
|
+
* codeSpanContent() so it also cannot break out of its code span.
|
|
206
|
+
*
|
|
207
|
+
* Exported (#1331) so direct `createFinding` callers can build a body that
|
|
208
|
+
* passes its sentinel check instead of hand-writing the sentinel line.
|
|
188
209
|
*
|
|
189
210
|
* @param {object} finding
|
|
190
211
|
* @param {string} fp - 16-char hex fingerprint
|
|
191
212
|
* @returns {string}
|
|
192
213
|
*/
|
|
193
|
-
function buildIssueBody(finding, fp) {
|
|
214
|
+
export function buildIssueBody(finding, fp) {
|
|
194
215
|
const safeRecommendation = sanitizeRecommendation(finding.recommendation);
|
|
195
216
|
const lines = [
|
|
196
|
-
finding.description,
|
|
217
|
+
sanitizeRecommendation(finding.description),
|
|
197
218
|
'',
|
|
198
219
|
safeRecommendation ? `**Recommendation:** ${safeRecommendation}` : null,
|
|
199
220
|
'',
|
|
200
221
|
`**Fingerprint:** \`${fp}\``,
|
|
201
222
|
`**Severity:** ${finding.severity}`,
|
|
202
223
|
`**Check:** ${finding.checkId}`,
|
|
203
|
-
`**Locator:** \`${finding.locator}\``,
|
|
224
|
+
`**Locator:** \`${codeSpanContent(finding.locator)}\``,
|
|
204
225
|
];
|
|
205
226
|
return lines.filter((line) => line !== null).join('\n');
|
|
206
227
|
}
|
|
@@ -528,9 +549,12 @@ export async function listExistingFindings({
|
|
|
528
549
|
*
|
|
529
550
|
* @param {object} opts
|
|
530
551
|
* @param {string} [opts.project] - GitLab project path (--repo; #872: otherwise auto-detected)
|
|
531
|
-
* @param {string} opts.fingerprint - 16-hex fingerprint
|
|
552
|
+
* @param {string} opts.fingerprint - 16-hex fingerprint; NOT appended here — `body` must already carry it
|
|
532
553
|
* @param {string} opts.title - issue title (no [Test] prefix added here — caller decides)
|
|
533
|
-
* @param {string} opts.body - issue description body; must not exceed 65536 bytes (#389)
|
|
554
|
+
* @param {string} opts.body - issue description body; must not exceed 65536 bytes (#389) and must
|
|
555
|
+
* carry the `**Fingerprint:** \`<fingerprint>\`` sentinel for this same fingerprint (#1331) —
|
|
556
|
+
* build it with {@link buildIssueBody}
|
|
557
|
+
|
|
534
558
|
* @param {string} [opts.labels='from:test-runner'] - comma-separated label string
|
|
535
559
|
* @param {boolean} [opts.dryRun=false] - if true, return command without spawning
|
|
536
560
|
* @param {number} [opts.maxBuffer=4194304] - maxBuffer for execFile (4 MB, #389)
|
|
@@ -590,6 +614,19 @@ export async function createFinding({
|
|
|
590
614
|
const bodyLengthError = checkBodyLength(body);
|
|
591
615
|
if (bodyLengthError) return bodyLengthError;
|
|
592
616
|
|
|
617
|
+
// #1331: dedup reads the fingerprint back out of the body on the next run, and
|
|
618
|
+
// nothing here appends it. A body without the matching sentinel would file an
|
|
619
|
+
// issue no later run can recognise — every run would then open a duplicate.
|
|
620
|
+
if (extractFingerprintFromBody(body) !== fingerprint) {
|
|
621
|
+
return {
|
|
622
|
+
ok: false,
|
|
623
|
+
error: {
|
|
624
|
+
code: 'VALIDATION',
|
|
625
|
+
message: 'body must carry a **Fingerprint:** sentinel matching fingerprint (use buildIssueBody)',
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
593
630
|
const args = ['issue', 'create', '--title', title, '--label', labels, '--description', body];
|
|
594
631
|
|
|
595
632
|
// #872: explicit project always wins; otherwise auto-detect a --repo spec
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* Pane 3 (bottom-right) — vcs-aware CI watch (poll-loop from detectVcsCommand)
|
|
12
12
|
* Pane 4 (bottom-left) — tail -F .orchestrator/metrics/events.jsonl | jq select(wave|gate|spiral)
|
|
13
13
|
* Pane 5 (optional) — agent-status telemetry (#565), only when withStatusPane is true:
|
|
14
|
-
* poll-loop over
|
|
14
|
+
* poll-loop over readCurrentStatus() from scripts/lib/agent-status.mjs,
|
|
15
|
+
* which reports provenance (live-map / rebuilt-log / stale-cache) — #1342.
|
|
15
16
|
*
|
|
16
17
|
* Debug layout pane map (user-facing numbering):
|
|
17
18
|
* Pane 1 (top-left) — scratch shell (NO command — default tmux shell, AUQ-001 compliance)
|
|
@@ -58,6 +59,61 @@ function shellQuote(s) {
|
|
|
58
59
|
return "'" + String(s).replace(/'/g, "'\\''") + "'";
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
/** Poll interval (seconds) of the agent-status pane loop. */
|
|
63
|
+
const STATUS_PANE_POLL_SECONDS = 2;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build the ONE-SHOT render command of the agent-status pane (#1342).
|
|
67
|
+
*
|
|
68
|
+
* The pane used to `jq` `.orchestrator/runtime/agent-status-current.json` directly.
|
|
69
|
+
* That file is a REBUILDABLE CACHE, not the source of truth: after a lock timeout
|
|
70
|
+
* (or a death between the ledger append and the map write) it shows `running` for
|
|
71
|
+
* an agent whose ledger already says `completed`. So the pane now goes through
|
|
72
|
+
* `readCurrentStatus()` — the only reader that folds the ledger tail and reports
|
|
73
|
+
* PROVENANCE — and prints that provenance in its header line:
|
|
74
|
+
*
|
|
75
|
+
* agent-status · source=<live-map|rebuilt-log|stale-cache|absent> · at=<ISO|n/a>[ · STALE][ · DEGRADED: <reasons>]
|
|
76
|
+
*
|
|
77
|
+
* A `stale-cache` source or any `degraded` reason is marked in TEXT (leading `⚠`
|
|
78
|
+
* plus the words STALE / DEGRADED), never by colour alone. `absent` (nothing on
|
|
79
|
+
* disk yet) stays UNMARKED — a fresh repo is not a degradation (HR-101).
|
|
80
|
+
*
|
|
81
|
+
* Exported so a consumer test can run the render ONCE instead of the poll loop.
|
|
82
|
+
*
|
|
83
|
+
* Shell-shape notes (both load-bearing):
|
|
84
|
+
* - the `-e` script is wrapped in SINGLE quotes, so the JS below uses only
|
|
85
|
+
* double-quoted strings: inside single quotes no `$`, backtick or interactive
|
|
86
|
+
* `!` history expansion can touch it.
|
|
87
|
+
* - the module path is resolved from THIS file's own URL (never a hardcoded home
|
|
88
|
+
* path), exactly as the sibling pane commands stay relative-to-cwd.
|
|
89
|
+
*
|
|
90
|
+
* @returns {string} a single shell command; prints the pane body once and exits.
|
|
91
|
+
*/
|
|
92
|
+
export function buildStatusPaneRenderCommand() {
|
|
93
|
+
// `%27` guard: a single quote in the repo path would otherwise end the shell
|
|
94
|
+
// single-quoted `-e` argument.
|
|
95
|
+
const moduleHref = new URL('../agent-status.mjs', import.meta.url).href.replace(/'/g, '%27');
|
|
96
|
+
const js = [
|
|
97
|
+
`import {readCurrentStatus} from ${JSON.stringify(moduleHref)};`,
|
|
98
|
+
'const v=readCurrentStatus({repoRoot:process.cwd()});',
|
|
99
|
+
'const ids=Object.keys(v.entries).sort();',
|
|
100
|
+
// An EMPTY channel (no ledger yet, no cache yet) is the normal fresh-repo
|
|
101
|
+
// state, not a degradation — marking it would fire the warning on every
|
|
102
|
+
// session and teach the operator to ignore it (host-resources.md HR-101).
|
|
103
|
+
// `readCurrentStatus()` names that state `absent` and emits no `degraded`,
|
|
104
|
+
// so the unmarked case keys on the SOURCE, not on a reason allowlist.
|
|
105
|
+
'const bare=v.source==="absent";',
|
|
106
|
+
'const marks=[];',
|
|
107
|
+
'if(!bare&&v.source==="stale-cache")marks.push("STALE");',
|
|
108
|
+
'if(!bare&&v.degraded)marks.push("DEGRADED: "+v.degraded.reasons.join(","));',
|
|
109
|
+
'const head=(marks.length>0?"\u26a0 ":"")+"agent-status \u00b7 source="+v.source+" \u00b7 at="+(v.at||"n/a")+(marks.length>0?" \u00b7 "+marks.join(" \u00b7 "):"");',
|
|
110
|
+
'console.log(head);',
|
|
111
|
+
'if(ids.length===0)console.log("no agent-status yet \u2014 set persistence:true + run a wave (see skills/wave-executor/wave-loop.md \u00a7 3a-bis)");',
|
|
112
|
+
'for(const id of ids){const r=v.entries[id]||{};const what=typeof r.text==="string"?r.text:(r.step!==undefined?r.step+"/"+r.total+(r.label?" "+r.label:""):"unknown");console.log(id+" "+what+" "+(r.ts||"no-ts"));}',
|
|
113
|
+
].join('');
|
|
114
|
+
return `node --input-type=module -e '${js}'`;
|
|
115
|
+
}
|
|
116
|
+
|
|
61
117
|
// ---------------------------------------------------------------------------
|
|
62
118
|
// renderDefaultLayout
|
|
63
119
|
// ---------------------------------------------------------------------------
|
|
@@ -91,9 +147,11 @@ async function _renderDefaultLayoutInner({ sessionName, force, projectRoot, vcsC
|
|
|
91
147
|
const pane2Cmd = `tail -F ${stateMdPath}`;
|
|
92
148
|
const pane3Cmd = vcs.command;
|
|
93
149
|
const pane4Cmd = `tail -F .orchestrator/metrics/events.jsonl | jq --unbuffered 'select(.event | test("wave|gate|spiral"))'`;
|
|
94
|
-
// Pane 5 (optional, #565): poll
|
|
95
|
-
//
|
|
96
|
-
|
|
150
|
+
// Pane 5 (optional, #565 / #1342): poll `readCurrentStatus()` — NOT the cache
|
|
151
|
+
// file — so the pane can never present a stale `running` as live. The renderer
|
|
152
|
+
// prints its own "no agent-status yet" line when the channel is empty, and is
|
|
153
|
+
// exported (buildStatusPaneRenderCommand) so a test can run it once.
|
|
154
|
+
const pane5Cmd = `while true; do clear; ${buildStatusPaneRenderCommand()}; sleep ${STATUS_PANE_POLL_SECONDS}; done`;
|
|
97
155
|
|
|
98
156
|
// 4. Session-collision check
|
|
99
157
|
const collision = isSessionCollision(sessionName);
|