session-orchestrator 5.12.0 → 5.13.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 +2 -2
- package/.claude-plugin/plugin.json +4 -1
- package/.codex-plugin/agents/explorer.toml +1 -1
- package/.codex-plugin/agents/session-reviewer.toml +2 -2
- package/.codex-plugin/agents/wave-worker.toml +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +8 -0
- package/.orchestrator/policy/public-export.json +30 -0
- package/CHANGELOG.md +162 -4
- package/README.md +126 -127
- package/agents/analyst.md +7 -2
- package/agents/architect-reviewer.md +7 -2
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +9 -2
- package/agents/dialectic-deriver.md +9 -3
- package/agents/docs-writer.md +7 -2
- package/agents/eval-judge.md +7 -1
- package/agents/qa-strategist.md +7 -2
- package/agents/security-reviewer.md +9 -2
- package/agents/session-reviewer.md +18 -2
- package/agents/skill-applied-judge.md +7 -1
- package/agents/test-writer.md +9 -2
- package/agents/ui-developer.md +9 -2
- package/agents/ux-evaluator.md +6 -1
- package/commands/session.md +1 -1
- package/commands/templates-ack.md +89 -18
- package/docs/agent-authoring.md +30 -11
- package/docs/ci-setup.md +38 -2
- package/docs/codex-setup.md +33 -9
- package/docs/components.md +13 -2
- package/docs/cursor-setup.md +3 -3
- package/docs/events-schema.md +5 -4
- package/docs/evidence-candidates.md +63 -0
- package/docs/fleet-checkin-audit.md +7 -4
- package/docs/github-mirror-protection.md +150 -3
- package/docs/install.md +90 -17
- package/docs/instruction-delivery.md +1 -1
- package/docs/migration-v4.md +2 -2
- package/docs/mods.md +118 -0
- package/docs/owner-config-schema.md +1 -0
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/pm-skills-marketplace.md +4 -3
- package/docs/rule-authoring.md +24 -13
- package/docs/scope-collision-guard.md +2 -1
- package/docs/session-config-reference.md +49 -15
- package/docs/session-config-template.md +30 -22
- package/docs/sessions-ledger-repair.md +137 -0
- package/docs/vault-docs-architecture.md +1 -1
- package/hooks/_lib/hook-import-set.json +406 -73
- package/hooks/_lib/hook-root.mjs +301 -0
- package/hooks/_lib/lock-bootstrap.mjs +434 -4
- package/hooks/_lib/orchestrator-ignore.mjs +3 -0
- package/hooks/_lib/subagent-transcript.mjs +2 -2
- package/hooks/_lib/vcs-create-matcher.mjs +13 -17
- package/hooks/cwd-change-restore.mjs +5 -1
- package/hooks/enforce-commands.mjs +4 -3
- package/hooks/enforce-scope.mjs +11 -1
- package/hooks/hooks-codex.json +48 -2
- package/hooks/hooks.json +31 -31
- package/hooks/on-session-end.mjs +38 -15
- package/hooks/on-session-start.mjs +139 -49
- package/hooks/on-stop.mjs +11 -6
- package/hooks/post-bash-issue-budget-refund.mjs +6 -1
- package/hooks/post-bash-write-verify.mjs +8 -4
- package/hooks/post-edit-validate.mjs +45 -52
- package/hooks/post-subagent-discovery-validator.mjs +2 -2
- package/hooks/post-tool-batch-wave-signal.mjs +8 -4
- package/hooks/post-tool-failure-corrective-context.mjs +5 -1
- package/hooks/pre-bash-destructive-guard.mjs +220 -8
- package/hooks/pre-bash-issue-budget.mjs +6 -1
- package/hooks/pre-bash-sessions-ledger-guard.mjs +691 -123
- package/hooks/pre-bash-staging-fence.mjs +258 -14
- package/hooks/pre-bash-templates-first.mjs +28 -27
- package/hooks/run-node.sh +1 -1
- package/hooks/skill-invocation-telemetry.mjs +15 -8
- package/hooks/subagent-telemetry.mjs +77 -14
- package/hooks/wave-scope-commit-guard.mjs +143 -14
- package/mods/so-hud/.claude-plugin/plugin.json +11 -0
- package/mods/so-hud/README.md +57 -0
- package/mods/so-hud/hooks/beacon-io.mjs +101 -0
- package/mods/so-hud/hooks/heredoc-scan.ts +394 -0
- package/mods/so-hud/hooks/hooks.json +1 -0
- package/mods/so-hud/hooks/logic.ts +182 -0
- package/mods/so-hud/hooks/register.tsx +670 -0
- package/mods/so-hud/hooks/rules.ts +2998 -0
- package/mods/so-hud/tsconfig.json +8 -0
- package/mods/so-hud/types/index.d.ts +36 -0
- package/package.json +4 -2
- package/rules/_index.md +1 -1
- package/rules/always-on/ask-via-tool.md +1 -1
- package/rules/always-on/loop-and-monitor.md +17 -194
- package/rules/always-on/parallel-sessions.md +3 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +1 -1
- package/rules/opt-in-stack/security-web.md +5 -1
- package/scripts/agent-usage-census.mjs +79 -0
- package/scripts/apply-session-learnings.mjs +34 -6
- package/scripts/archive-closed-prds.mjs +21 -5
- package/scripts/autopilot.mjs +11 -5
- package/scripts/backfill-abandoned-sessions.mjs +13 -3
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +61 -6
- package/scripts/backfill-sessions.mjs +16 -2
- package/scripts/check-package-manager.mjs +26 -8
- package/scripts/check-sessions-integrity.mjs +4 -1
- package/scripts/ci/assert-vitest-green.mjs +3 -0
- package/scripts/ci/windows-smoke.mjs +201 -0
- package/scripts/close-precheck.mjs +55 -0
- package/scripts/dialectic-deriver.mjs +20 -12
- package/scripts/emit-event.mjs +2 -2
- package/scripts/emit-session.mjs +239 -20
- package/scripts/eval-session.mjs +17 -3
- package/scripts/export-hw-learnings.mjs +29 -2
- package/scripts/fleet-checkin-audit.mjs +8 -4
- package/scripts/fleet-checkin.mjs +39 -27
- package/scripts/gc-stale-worktrees.mjs +6 -1
- package/scripts/github-export.mjs +128 -0
- package/scripts/github-protection-audit.mjs +3 -1
- package/scripts/handover-queue-check.mjs +209 -0
- package/scripts/harness-audit.mjs +6 -4
- package/scripts/lib/agent-frontmatter.mjs +57 -10
- package/scripts/lib/agent-status.mjs +125 -7
- package/scripts/lib/agent-usage-census.mjs +394 -0
- package/scripts/lib/auto-dialectic.mjs +17 -10
- package/scripts/lib/auto-dream.mjs +22 -14
- package/scripts/lib/autopilot/loop.mjs +3 -1
- package/scripts/lib/autopilot/recent-runs.mjs +4 -2
- package/scripts/lib/autopilot/stall-sampler.mjs +2 -1
- package/scripts/lib/autopilot/telemetry.mjs +5 -1
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/baseline-archetypes.mjs +10 -2
- package/scripts/lib/blocked-commands-policy.mjs +4 -3
- package/scripts/lib/build-live-signals.mjs +13 -9
- package/scripts/lib/ci-status-banner.mjs +329 -24
- package/scripts/lib/codex/plugin-contract.mjs +8 -0
- package/scripts/lib/cold-start-detector.mjs +5 -2
- package/scripts/lib/command-blocker.mjs +603 -43
- package/scripts/lib/common.mjs +3 -2
- package/scripts/lib/config/cross-repo.mjs +74 -7
- package/scripts/lib/config/host-paths.mjs +6 -2
- package/scripts/lib/config/section-extractor.mjs +65 -11
- package/scripts/lib/config/template-keys.mjs +153 -0
- package/scripts/lib/config/vault-integration.mjs +1 -0
- package/scripts/lib/config.mjs +2 -2
- package/scripts/lib/coordinator-snapshot.mjs +1 -1
- package/scripts/lib/discovery/triage-state.mjs +2 -1
- package/scripts/lib/dispatcher/rank.mjs +5 -7
- package/scripts/lib/ecosystem-health.mjs +2 -2
- package/scripts/lib/eval/engine.mjs +3 -2
- package/scripts/lib/eval/session-resolve.mjs +6 -2
- package/scripts/lib/eval/sink.mjs +4 -3
- package/scripts/lib/events-retention-banner.mjs +3 -2
- package/scripts/lib/events-schema.mjs +10 -0
- package/scripts/lib/events.mjs +23 -12
- package/scripts/lib/evolve/autonomy-verdict.mjs +4 -9
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +8 -18
- package/scripts/lib/fleet-protocol.mjs +4 -0
- package/scripts/lib/gates/gate-full.mjs +172 -11
- package/scripts/lib/gates/gate-helpers.mjs +236 -23
- package/scripts/lib/git-config-drift.mjs +141 -7
- package/scripts/lib/github-export.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +16 -5
- package/scripts/lib/handover-queue-marks.mjs +159 -0
- package/scripts/lib/hardware-pattern-detector.mjs +103 -13
- package/scripts/lib/harness-audit/categories/category1.mjs +16 -4
- package/scripts/lib/harness-audit/categories/category4.mjs +11 -8
- package/scripts/lib/harness-audit/categories/category6.mjs +25 -5
- package/scripts/lib/harness-audit/categories/category9.mjs +10 -4
- package/scripts/lib/host-identity.mjs +9 -5
- package/scripts/lib/http-request.mjs +64 -0
- package/scripts/lib/instruction-budget-guard.mjs +30 -22
- package/scripts/lib/io.mjs +6 -3
- package/scripts/lib/issue-budget-reconcile.mjs +3 -2
- package/scripts/lib/issue-budget.mjs +36 -7
- package/scripts/lib/learnings/evidence-candidates.mjs +154 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +99 -7
- package/scripts/lib/learnings/io.mjs +289 -15
- package/scripts/lib/learnings/select.mjs +53 -4
- package/scripts/lib/learnings/store-role.mjs +209 -0
- package/scripts/lib/learnings/surface.mjs +46 -7
- package/scripts/lib/ledger-path.mjs +113 -0
- package/scripts/lib/maintenance-due-banner.mjs +63 -12
- package/scripts/lib/memory-banner.mjs +5 -6
- package/scripts/lib/memory-proposals/collector.mjs +5 -2
- package/scripts/lib/memory-proposals/sink.mjs +7 -4
- package/scripts/lib/memory-proposals/store.mjs +22 -5
- package/scripts/lib/mode-selector-accuracy.mjs +2 -1
- package/scripts/lib/no-follow-open.mjs +57 -0
- package/scripts/lib/orphan-reaper/reaper-audit.mjs +4 -2
- package/scripts/lib/own-lock-root.mjs +95 -0
- package/scripts/lib/owner-interview.mjs +2 -1
- package/scripts/lib/owner-yaml.mjs +70 -7
- package/scripts/lib/platform.mjs +161 -9
- package/scripts/lib/plugin-update-banner.mjs +15 -11
- package/scripts/lib/pre-push-local-partial.mjs +117 -0
- package/scripts/lib/pre-push-remote-gate.mjs +151 -25
- package/scripts/lib/process-group.mjs +270 -34
- package/scripts/lib/project-hygiene.mjs +28 -0
- package/scripts/lib/quality-gate.mjs +33 -9
- package/scripts/lib/reconcile/backlog.mjs +29 -15
- package/scripts/lib/reconcile/engine.mjs +26 -2
- package/scripts/lib/reconcile/idempotency.mjs +65 -3
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +53 -12
- package/scripts/lib/reconcile/verdict-ledger.mjs +224 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +40 -6
- package/scripts/lib/resource-probe/evaluate.mjs +32 -9
- package/scripts/lib/resource-probe/probe-platform.mjs +3 -1
- package/scripts/lib/rule-loader.mjs +20 -10
- package/scripts/lib/scope-baseline.mjs +123 -8
- package/scripts/lib/scope-echo.mjs +5 -2
- package/scripts/lib/scope-gate.mjs +55 -20
- package/scripts/lib/session-close-backfill.mjs +75 -7
- package/scripts/lib/session-end/close-precheck.mjs +151 -0
- package/scripts/lib/session-end/phase-skip.mjs +8 -7
- package/scripts/lib/session-end/tail-runner.mjs +7 -2
- package/scripts/lib/session-id.mjs +9 -1
- package/scripts/lib/session-identity/own-session.mjs +63 -0
- package/scripts/lib/session-lock.mjs +114 -0
- package/scripts/lib/session-record-repair.mjs +245 -35
- package/scripts/lib/session-registry.mjs +72 -9
- package/scripts/lib/session-schema/constants.mjs +9 -0
- package/scripts/lib/session-schema/filters.mjs +56 -1
- package/scripts/lib/session-schema/validator.mjs +15 -5
- package/scripts/lib/session-start-probes.mjs +149 -22
- package/scripts/lib/session-token-rollup.mjs +2 -1
- package/scripts/lib/session-work-trees.mjs +166 -0
- package/scripts/lib/sessions-canonical.mjs +61 -3
- package/scripts/lib/sessions-integrity-banner.mjs +17 -39
- package/scripts/lib/sessions-staleness-banner.mjs +8 -5
- package/scripts/lib/sh-quote.mjs +1 -0
- package/scripts/lib/skill-evidence-window.mjs +5 -1
- package/scripts/lib/skill-health/join.mjs +10 -9
- package/scripts/lib/skill-invocations-schema.mjs +13 -0
- package/scripts/lib/skill-judgments-schema.mjs +6 -2
- package/scripts/lib/snapshot-probe.mjs +414 -0
- package/scripts/lib/so-hud/collect.mjs +49 -5
- package/scripts/lib/so-hud/runtime.mjs +15 -4
- package/scripts/lib/spiral-carryover.mjs +8 -1
- package/scripts/lib/state-md/frontmatter-mutators.mjs +266 -8
- package/scripts/lib/state-md/idle-reset.mjs +14 -5
- package/scripts/lib/state-md/mission-status.mjs +5 -1
- package/scripts/lib/state-md-peer-guard.mjs +2 -0
- package/scripts/lib/state-md.mjs +31 -8
- package/scripts/lib/sunset/walker.mjs +4 -3
- package/scripts/lib/telemetry/consent.mjs +4 -2
- package/scripts/lib/telemetry/queue.mjs +3 -3
- package/scripts/lib/telemetry/sync.mjs +45 -9
- package/scripts/lib/test-runner/artifact-paths.mjs +10 -2
- package/scripts/lib/tmux-layout/layouts.mjs +20 -3
- package/scripts/lib/ux-grill/paths.mjs +5 -1
- package/scripts/lib/validate/check-agents.mjs +37 -6
- package/scripts/lib/validate/check-component-paths.mjs +25 -1
- package/scripts/lib/validate/check-guard-requires-parity.mjs +3 -3
- package/scripts/lib/validate/check-hooks-symmetry.mjs +53 -32
- package/scripts/lib/validate/check-json-files.mjs +3 -1
- package/scripts/lib/validate/check-learning-provenance.mjs +3 -2
- package/scripts/lib/validate/check-learnings-shell-writes.mjs +10 -4
- package/scripts/lib/validate/check-metrics-ledger-root.mjs +234 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +159 -11
- package/scripts/lib/validate/check-test-posix-shell.mjs +246 -0
- package/scripts/lib/validate/check-unwired-features.mjs +4 -2
- package/scripts/lib/validate/check-vcs-repo-flag-positional.mjs +58 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +259 -73
- package/scripts/lib/validate/check-workflow-continue-on-error.mjs +63 -0
- package/scripts/lib/validate/confidential-names.mjs +3 -1
- package/scripts/lib/vault-mirror/migrate-ids.mjs +321 -0
- package/scripts/lib/vault-mirror/namespace.mjs +19 -7
- package/scripts/lib/vault-mirror/process.mjs +56 -27
- package/scripts/lib/vault-mirror/prune.mjs +5 -2
- package/scripts/lib/vault-mirror/render-learnings.mjs +3 -3
- package/scripts/lib/vault-mirror/render-sessions.mjs +36 -53
- package/scripts/lib/vault-mirror/retention.mjs +2 -2
- package/scripts/lib/vault-mirror/utils.mjs +79 -0
- package/scripts/lib/vault-staleness-banner.mjs +4 -7
- package/scripts/lib/vault-status/board-writer.mjs +2 -1
- package/scripts/lib/vault-status/narrative-mirror.mjs +66 -7
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +20 -0
- package/scripts/lib/win-process-tree.mjs +137 -0
- package/scripts/lib/workspace.mjs +1 -1
- package/scripts/lib/worktree/constants.mjs +1 -1
- package/scripts/lib/worktree/lifecycle.mjs +1 -1
- package/scripts/lib/worktree/listing.mjs +1 -1
- package/scripts/lib/zx.mjs +50 -0
- package/scripts/mcp-server.mjs +16 -7
- package/scripts/mcp-server.sh +13 -83
- package/scripts/measure-policy-cache-effectiveness.mjs +7 -5
- package/scripts/memory-propose.mjs +10 -3
- package/scripts/migrate-cold-start-seed.mjs +7 -0
- package/scripts/migrate-legacy-learnings.sh +22 -0
- package/scripts/migrate-sessions-jsonl.mjs +16 -2
- package/scripts/migrate-vault-paths.mjs +29 -13
- package/scripts/parse-config.mjs +71 -14
- package/scripts/print-learnings-index.mjs +25 -2
- package/scripts/release-session-lock.mjs +94 -7
- package/scripts/release.mjs +451 -24
- package/scripts/relocate-vault-corpus.mjs +11 -7
- package/scripts/repair-invalid-sessions.mjs +68 -3
- package/scripts/resolve-session-invocation.mjs +10 -17
- package/scripts/run-migrate-v2-cross-repo.mjs +56 -1
- package/scripts/run-quality-gate.mjs +33 -7
- package/scripts/self-update.mjs +56 -7
- package/scripts/session-commit-files.mjs +133 -0
- package/scripts/site-numbers.mjs +52 -9
- package/scripts/snapshot-probe.mjs +156 -0
- package/scripts/so-hud.mjs +29 -6
- package/scripts/state-md-idle-reset.mjs +2 -1
- package/scripts/sweep-expired-learnings.mjs +54 -6
- package/scripts/sweep-expired-rules.mjs +4 -1
- package/scripts/validate-plugin.mjs +22 -0
- package/scripts/vault-integration-watcher.mjs +54 -6
- package/scripts/vault-mirror.mjs +9 -0
- package/skills/_shared/fleet-protocol.md +142 -1
- package/skills/_shared/instruction-file-resolution.md +1 -1
- package/skills/_shared/monitor-patterns.md +44 -25
- package/skills/_shared/platform-tools.md +7 -0
- package/skills/_shared/state-ownership.md +3 -2
- package/skills/architecture/references/domain-model.md +1 -1
- package/skills/bootstrap/SKILL.md +3 -1
- package/skills/bootstrap/_shared-template.md +4 -4
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/bootstrap/private-contract.md +1 -1
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +17 -9
- package/skills/bootstrap/templates/agents/project-code-review.md +5 -1
- package/skills/bootstrap/templates/agents/project-discovery.md +5 -1
- package/skills/bootstrap/templates/agents/project-quality-gate.md +5 -1
- package/skills/claude-md-drift-check/SKILL.md +3 -3
- package/skills/claude-md-drift-check/checker.mjs +61 -101
- package/skills/close/SKILL.md +20 -7
- package/skills/discovery/probes/docs-staleness.mjs +7 -5
- package/skills/discovery/probes/frontend-slop.mjs +7 -3
- package/skills/discovery/probes/ssot-code-diff.mjs +5 -3
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +10 -8
- package/skills/discovery/probes/vault-narrative-staleness.mjs +30 -10
- package/skills/discovery/probes/vault-staleness.mjs +5 -3
- package/skills/discovery/probes-session.md +3 -0
- package/skills/discovery/probes-vault.md +2 -0
- package/skills/dispatcher/SKILL.md +1 -1
- package/skills/eval/SKILL.md +3 -1
- package/skills/evolve/SKILL.md +2 -0
- package/skills/evolve/references/evolve-analyze-mode.md +3 -3
- package/skills/evolve/references/evolve-dialectic-mode.md +1 -1
- package/skills/gitlab-ops/SKILL.md +7 -6
- package/skills/gitlab-portfolio/SKILL.md +2 -2
- package/skills/go/SKILL.md +13 -1
- package/skills/npm-publish/SKILL.md +5 -2
- package/skills/plan/SKILL.md +23 -10
- package/skills/plan/mode-feature.md +13 -8
- package/skills/plan/mode-new.md +6 -14
- package/skills/plan/prd-feature-template.md +19 -0
- package/skills/plan/prd-full-template.md +12 -5
- package/skills/plan/prd-premortem-panel.md +48 -0
- package/skills/plan/prd-reviewer-prompt.md +11 -3
- package/skills/plan/soul.md +1 -1
- package/skills/portfolio/SKILL.md +5 -6
- package/skills/quality-gates/SKILL.md +2 -4
- package/skills/reconcile/SKILL.md +27 -4
- package/skills/release/SKILL.md +16 -8
- package/skills/session-end/SKILL.md +94 -11
- package/skills/session-end/phase-3-2-docs-verification.md +1 -0
- package/skills/session-end/phase-3-6-tail.md +11 -7
- package/skills/session-end/phase-3-7a-recommendations.md +9 -2
- package/skills/session-end/plan-verification.md +16 -1
- package/skills/session-end/references/phase-3-documentation-updates.md +14 -12
- package/skills/session-end/references/phase-5-issue-cleanup.md +1 -1
- package/skills/session-end/references/session-summary-template.md +4 -0
- package/skills/session-end/session-metrics-write.md +7 -4
- package/skills/session-plan/SKILL.md +5 -3
- package/skills/session-start/SKILL.md +9 -3
- package/skills/session-start/phase-4-5-resource-health.md +11 -5
- package/skills/session-start/phase-8-5-express-path.md +4 -3
- package/skills/session-start/references/phase-1-2-session-lock.md +7 -5
- package/skills/session-start/references/phase-1-5-session-continuity.md +2 -2
- package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +3 -2
- package/skills/tmux-layout/SKILL.md +2 -2
- package/skills/vault-sync/validator.mjs +3 -2
- package/skills/wave-executor/SKILL.md +5 -4
- package/skills/wave-executor/references/wave-executor-state-init.md +3 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +11 -5
- package/skills/wave-executor/references/wave-loop-review.md +13 -5
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +1 -1
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +0 -47
- package/assets/wave-lifecycle.svg +0 -98
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-test-posix-shell.mjs — tests must not spawn a POSIX shell by bare name.
|
|
4
|
+
*
|
|
5
|
+
* WHY. On native Windows `sh` is not on PATH and `bash` resolves to
|
|
6
|
+
* C:\Windows\System32\bash.exe, which is WSL and cannot see Windows paths
|
|
7
|
+
* (measured 2026-10-22 on the Windows 11 test PC, `where sh & where bash`,
|
|
8
|
+
* SO#1613). A test that runs `spawnSync('sh', …)` or `execFileSync('/bin/bash', …)`
|
|
9
|
+
* is therefore red on Windows for a reason that has nothing to do with the code
|
|
10
|
+
* under test. The native run at fd8d3673 counted 301 red tests in exactly this
|
|
11
|
+
* class; the same session moved ~25 test files to `posixShell()` from
|
|
12
|
+
* `tests/_helpers/posix-shell.mjs`, which returns the name unchanged off Windows
|
|
13
|
+
* and Git Bash's sh.exe/bash.exe on win32. This check keeps the class from
|
|
14
|
+
* coming back.
|
|
15
|
+
*
|
|
16
|
+
* RULE. In tracked `tests/**\/*.mjs`, a finding is:
|
|
17
|
+
* - a call to `spawn` / `spawnSync` / `execFile` / `execFileSync` (bare or as a
|
|
18
|
+
* member, e.g. `cp.spawnSync`) whose FIRST argument is the string literal
|
|
19
|
+
* `sh`, `bash`, `/bin/sh` or `/bin/bash` in any quote style;
|
|
20
|
+
* - a call to `exec` / `execSync` whose command string literal starts with
|
|
21
|
+
* `sh `, `bash `, `/bin/sh ` or `/bin/bash `.
|
|
22
|
+
* A call through `posixShell(...)` is never a finding (its first argument is not
|
|
23
|
+
* a literal). `tests/_helpers/posix-shell.mjs` itself is exempt.
|
|
24
|
+
*
|
|
25
|
+
* METHOD. Textual, over the shared lexer `scripts/lib/js-mask.mjs`: the call
|
|
26
|
+
* name and its opening quote are matched on the fully masked source (comments
|
|
27
|
+
* and literal interiors blanked), so a commented-out spawn and fixture source
|
|
28
|
+
* embedded in a template literal are not scanned as code; the literal VALUE is
|
|
29
|
+
* then read at the same offset from the comments-only mask (offsets are
|
|
30
|
+
* preserved by both modes).
|
|
31
|
+
* Named ceiling (BV-004): a shell name passed through a variable
|
|
32
|
+
* (`const SH = 'sh'; spawnSync(SH, …)`) is not seen. REVISIT if a native
|
|
33
|
+
* Windows run shows that shape red.
|
|
34
|
+
*
|
|
35
|
+
* BASELINE. Three files are changed by open merge requests (!239/!237, !240) at the
|
|
36
|
+
* time this check landed, so the #1613 session left them untouched; each is
|
|
37
|
+
* baselined below (WARN, not FAIL) and gets converted after those MRs land. An
|
|
38
|
+
* entry whose file no longer has a finding FAILs as `baseline-stale` so a
|
|
39
|
+
* converted file does not leave a permanent exemption behind; an entry with an
|
|
40
|
+
* empty reason FAILs as `baseline-missing-reason` (same discipline as
|
|
41
|
+
* `check-hooks-emit-event-guard.mjs`). Every other site FAILs.
|
|
42
|
+
*
|
|
43
|
+
* Usage: check-test-posix-shell.mjs <repo-root>
|
|
44
|
+
* Output: ` PASS: …` / ` WARN: …` / ` FAIL: …` lines, then
|
|
45
|
+
* `Results: N passed, M failed`. Exit 0 = clean (baselined WARNs allowed),
|
|
46
|
+
* 1 = at least one new finding or baseline defect, 2 = tool error.
|
|
47
|
+
*
|
|
48
|
+
* Import-safety: importing this module executes nothing — the isMain guard at
|
|
49
|
+
* the bottom is the only side-effecting path.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
import { readFileSync } from 'node:fs';
|
|
53
|
+
import path from 'node:path';
|
|
54
|
+
import { maskSource } from '../js-mask.mjs';
|
|
55
|
+
import { listRepoFiles } from './repo-files.mjs';
|
|
56
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
57
|
+
|
|
58
|
+
const SCAN_DIR = 'tests';
|
|
59
|
+
|
|
60
|
+
/** The helper that resolves the shell; it may name the shells itself. */
|
|
61
|
+
const EXEMPT_FILES = new Set(['tests/_helpers/posix-shell.mjs']);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Baseline — Map<repo-relative file, reason>. Keyed per file, not per line:
|
|
65
|
+
* the open MRs reshape these files, and a line key would silently stop matching.
|
|
66
|
+
*/
|
|
67
|
+
const BASELINE_FILES = new Map([
|
|
68
|
+
[
|
|
69
|
+
'tests/skills/tmux-layout.test.mjs',
|
|
70
|
+
'#1613: file is changed by an open MR (!240); convert to posixShell() after it lands',
|
|
71
|
+
],
|
|
72
|
+
[
|
|
73
|
+
'tests/so-hud/rules.test.mjs',
|
|
74
|
+
'#1613: file is changed by an open MR (!239/!237); convert to posixShell() after it lands',
|
|
75
|
+
],
|
|
76
|
+
[
|
|
77
|
+
'tests/scripts/sweep-expired-learnings-cli.test.mjs',
|
|
78
|
+
'#1613: file is changed by an open MR (!239/!237); convert to posixShell() after it lands',
|
|
79
|
+
],
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
const SHELL_NAMES = new Set(['sh', 'bash', '/bin/sh', '/bin/bash']);
|
|
83
|
+
const SHELL_COMMAND_PREFIX_RE = /^(?:\/bin\/)?(?:sh|bash) /;
|
|
84
|
+
|
|
85
|
+
/** Call name plus open paren plus a quote, on the masked source. */
|
|
86
|
+
const CALL_RE = /\b(spawn|spawnSync|execFile|execFileSync|exec|execSync)\s*\(\s*(['"`])/g;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Read the string literal that opens at `start` (a quote char) in `text`.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} text comments-only masked source
|
|
92
|
+
* @param {number} start offset of the opening quote
|
|
93
|
+
* @returns {string | null} the literal's value, or null when it is not a
|
|
94
|
+
* single-line literal without `${` interpolation
|
|
95
|
+
*/
|
|
96
|
+
function literalAt(text, start) {
|
|
97
|
+
const quote = text[start];
|
|
98
|
+
const end = text.indexOf(quote, start + 1);
|
|
99
|
+
if (end === -1) return null;
|
|
100
|
+
const value = text.slice(start + 1, end);
|
|
101
|
+
if (value.includes('\n') || value.includes('${')) return null;
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Find bare-shell spawn call sites in one source text.
|
|
107
|
+
*
|
|
108
|
+
* @param {string} source
|
|
109
|
+
* @returns {{line: number, call: string, shell: string}[]}
|
|
110
|
+
*/
|
|
111
|
+
export function findBareShellCalls(source) {
|
|
112
|
+
const masked = maskSource(source);
|
|
113
|
+
const literals = maskSource(source, { keepLiterals: true });
|
|
114
|
+
/** @type {{line: number, call: string, shell: string}[]} */
|
|
115
|
+
const calls = [];
|
|
116
|
+
for (const m of masked.matchAll(CALL_RE)) {
|
|
117
|
+
const call = m[1];
|
|
118
|
+
const quoteAt = m.index + m[0].length - 1;
|
|
119
|
+
const value = literalAt(literals, quoteAt);
|
|
120
|
+
if (value === null) continue;
|
|
121
|
+
const isExecFamily = call === 'exec' || call === 'execSync';
|
|
122
|
+
const hit = isExecFamily ? SHELL_COMMAND_PREFIX_RE.test(value) : SHELL_NAMES.has(value);
|
|
123
|
+
if (!hit) continue;
|
|
124
|
+
const line = source.slice(0, m.index).split('\n').length;
|
|
125
|
+
calls.push({ line, call, shell: isExecFamily ? value.split(' ')[0] : value });
|
|
126
|
+
}
|
|
127
|
+
return calls;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @typedef {{kind: 'bare-shell', file: string, line: number, call: string, shell: string, baselined: boolean}} ShellFinding
|
|
132
|
+
* @typedef {{kind: 'baseline-stale' | 'baseline-missing-reason', file: string, message: string}} BaselineFinding
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Scan tracked `tests/**\/*.mjs` under `repoRoot`.
|
|
137
|
+
*
|
|
138
|
+
* @param {string} repoRoot
|
|
139
|
+
* @param {{baseline?: Map<string, string>}} [options] `baseline` defaults to the
|
|
140
|
+
* shipped {@link BASELINE_FILES}; injectable so tests exercise the baseline
|
|
141
|
+
* paths against a tmp fixture.
|
|
142
|
+
* @returns {{findings: (ShellFinding | BaselineFinding)[], readErrors: {file: string, message: string}[]}}
|
|
143
|
+
*/
|
|
144
|
+
export function scanTestPosixShell(repoRoot, { baseline = BASELINE_FILES } = {}) {
|
|
145
|
+
const files = listRepoFiles(repoRoot, { dirs: [SCAN_DIR], exts: ['mjs'] });
|
|
146
|
+
/** @type {(ShellFinding | BaselineFinding)[]} */
|
|
147
|
+
const findings = [];
|
|
148
|
+
/** @type {{file: string, message: string}[]} */
|
|
149
|
+
const readErrors = [];
|
|
150
|
+
/** @type {Set<string>} */
|
|
151
|
+
const matchedBaseline = new Set();
|
|
152
|
+
|
|
153
|
+
for (const absFile of files) {
|
|
154
|
+
const rel = path.relative(repoRoot, absFile).split(path.sep).join('/');
|
|
155
|
+
if (EXEMPT_FILES.has(rel)) continue;
|
|
156
|
+
let source;
|
|
157
|
+
try {
|
|
158
|
+
source = readFileSync(absFile, 'utf8');
|
|
159
|
+
} catch (err) {
|
|
160
|
+
readErrors.push({ file: rel, message: `unreadable: ${err?.message ?? String(err)}` });
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (!/\b(?:spawn|exec)/.test(source)) continue; // cheap textual pre-filter
|
|
164
|
+
|
|
165
|
+
const calls = findBareShellCalls(source);
|
|
166
|
+
if (calls.length === 0) continue;
|
|
167
|
+
const baselined = baseline.has(rel);
|
|
168
|
+
if (baselined) {
|
|
169
|
+
matchedBaseline.add(rel);
|
|
170
|
+
if (String(baseline.get(rel) ?? '').trim() === '') {
|
|
171
|
+
findings.push({
|
|
172
|
+
kind: 'baseline-missing-reason',
|
|
173
|
+
file: rel,
|
|
174
|
+
message: 'baseline entry has no reason — name the linked issue or remove the entry',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
for (const c of calls) findings.push({ kind: 'bare-shell', file: rel, ...c, baselined });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (const file of baseline.keys()) {
|
|
182
|
+
if (matchedBaseline.has(file)) continue;
|
|
183
|
+
findings.push({
|
|
184
|
+
kind: 'baseline-stale',
|
|
185
|
+
file,
|
|
186
|
+
message: 'baseline entry no longer has a bare shell spawn (converted or removed) — remove the entry',
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return { findings, readErrors };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Run the check and print the validate-plugin line vocabulary.
|
|
195
|
+
*
|
|
196
|
+
* @param {string} repoRoot
|
|
197
|
+
* @param {{baseline?: Map<string, string>}} [options] forwarded to {@link scanTestPosixShell}
|
|
198
|
+
* @returns {number} 0 = clean, 1 = new finding(s), 2 = tool error
|
|
199
|
+
*/
|
|
200
|
+
export function runCheckTestPosixShell(repoRoot, options) {
|
|
201
|
+
console.log('--- Check: tests spawn a POSIX shell via posixShell(), not by bare name (#1613) ---');
|
|
202
|
+
|
|
203
|
+
const { findings, readErrors } = scanTestPosixShell(repoRoot, options);
|
|
204
|
+
if (readErrors.length > 0) {
|
|
205
|
+
for (const e of readErrors) console.log(` FAIL: ${e.file} — ${e.message}`);
|
|
206
|
+
console.log('');
|
|
207
|
+
console.log(`Results: 0 passed, ${readErrors.length} failed`);
|
|
208
|
+
return 2;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const shellFindings = /** @type {ShellFinding[]} */ (findings.filter((f) => f.kind === 'bare-shell'));
|
|
212
|
+
const baselineIssues = /** @type {BaselineFinding[]} */ (findings.filter((f) => f.kind !== 'bare-shell'));
|
|
213
|
+
const fresh = shellFindings.filter((f) => !f.baselined);
|
|
214
|
+
const old = shellFindings.filter((f) => f.baselined);
|
|
215
|
+
|
|
216
|
+
if (fresh.length === 0 && baselineIssues.length === 0) {
|
|
217
|
+
console.log(
|
|
218
|
+
old.length === 0
|
|
219
|
+
? ' PASS: no test spawns sh/bash by bare name'
|
|
220
|
+
: ` PASS: no NEW bare sh/bash spawn in tests/ (${old.length} pre-existing, baselined — see #1613)`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
for (const f of old) {
|
|
224
|
+
console.log(` WARN: ${f.file}:${f.line} — ${f.call}('${f.shell}' …) by bare name (baselined #1613, file is in an open MR)`);
|
|
225
|
+
}
|
|
226
|
+
for (const f of fresh) {
|
|
227
|
+
console.log(
|
|
228
|
+
` FAIL: ${f.file}:${f.line} — ${f.call}('${f.shell}' …) by bare name; on native Windows sh is not on PATH and bash is WSL — use posixShell() from tests/_helpers/posix-shell.mjs`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
for (const f of baselineIssues) console.log(` FAIL: ${f.file} — ${f.message} (${f.kind})`);
|
|
232
|
+
|
|
233
|
+
const failCount = fresh.length + baselineIssues.length;
|
|
234
|
+
console.log('');
|
|
235
|
+
console.log(`Results: ${failCount === 0 ? 1 : 0} passed, ${failCount} failed`);
|
|
236
|
+
return failCount > 0 ? 1 : 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (isMainModule(import.meta.url)) {
|
|
240
|
+
const root = process.argv[2];
|
|
241
|
+
if (!root) {
|
|
242
|
+
console.error('Usage: check-test-posix-shell.mjs <repo-root>');
|
|
243
|
+
process.exit(2);
|
|
244
|
+
}
|
|
245
|
+
process.exit(runCheckTestPosixShell(path.resolve(root)));
|
|
246
|
+
}
|
|
@@ -292,6 +292,8 @@ import path from 'node:path';
|
|
|
292
292
|
|
|
293
293
|
import { sizingSubject } from '../learnings/sizing-subject.mjs';
|
|
294
294
|
import { isMainModule } from '../is-main-module.mjs';
|
|
295
|
+
import { resolveLedgerPath } from '../ledger-path.mjs';
|
|
296
|
+
import { resolveEventsLedgerRoot } from '../platform.mjs';
|
|
295
297
|
|
|
296
298
|
/** Documented config surface — every `yaml` fence in this file is a declaration. */
|
|
297
299
|
const TEMPLATE_REL = 'docs/session-config-template.md';
|
|
@@ -1248,7 +1250,7 @@ export function collectHandKeyedLearningSubjects(pluginRoot) {
|
|
|
1248
1250
|
let judged = 0;
|
|
1249
1251
|
let unattributed = 0;
|
|
1250
1252
|
|
|
1251
|
-
const learnings = readJsonl(
|
|
1253
|
+
const learnings = readJsonl(resolveLedgerPath(LEARNINGS_REL, pluginRoot));
|
|
1252
1254
|
if (learnings.length === 0) return { findings, scanned: { judged, unattributed } };
|
|
1253
1255
|
|
|
1254
1256
|
// A session record can be addressed by either identity form (see
|
|
@@ -1257,7 +1259,7 @@ export function collectHandKeyedLearningSubjects(pluginRoot) {
|
|
|
1257
1259
|
// a resolvable record is never mis-counted as unattributed.
|
|
1258
1260
|
/** @type {Map<string, Record<string, unknown>>} */
|
|
1259
1261
|
const sessionsById = new Map();
|
|
1260
|
-
for (const record of readJsonl(path.join(pluginRoot, SESSIONS_REL))) {
|
|
1262
|
+
for (const record of readJsonl(path.join(resolveEventsLedgerRoot(pluginRoot), SESSIONS_REL))) {
|
|
1261
1263
|
for (const field of ['session_id', 'semantic_session_id']) {
|
|
1262
1264
|
const id = record[field];
|
|
1263
1265
|
if (typeof id === 'string' && id !== '' && !sessionsById.has(id)) sessionsById.set(id, record);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Separate WARN-only check for gh pr commands rejected with --repo/-R (#1024).
|
|
4
|
+
* Shares corpus discovery and call-shape parsing with the bare-repo census,
|
|
5
|
+
* but its findings advise naming the PR or dropping the flag, never adding it.
|
|
6
|
+
* Coverage ceilings (literal argv, shell fences/strings) are shared with that census.
|
|
7
|
+
*/
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { inspectVcsRepoFlag } from './check-vcs-repo-flag.mjs';
|
|
10
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
11
|
+
|
|
12
|
+
/** @param {string} pluginRoot independent check envelope */
|
|
13
|
+
export function inspectVcsRepoFlagPositional(pluginRoot) {
|
|
14
|
+
const result = inspectVcsRepoFlag(pluginRoot, { rejected: true });
|
|
15
|
+
return {
|
|
16
|
+
ok: result.ok,
|
|
17
|
+
toolError: result.toolError,
|
|
18
|
+
summary: {
|
|
19
|
+
filesScanned: result.summary.filesScanned,
|
|
20
|
+
unresolvedArgv: result.summary.unresolvedArgv,
|
|
21
|
+
findings: result.findings.length,
|
|
22
|
+
byKind: result.summary.byKind,
|
|
23
|
+
},
|
|
24
|
+
findings: result.findings,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isMainModule(import.meta.url)) {
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
const usage =
|
|
31
|
+
'Usage: check-vcs-repo-flag-positional.mjs [<plugin-root>] [--json]\n' +
|
|
32
|
+
'Exit: 0 check completed (findings WARN-only) · 1 usage error · 2 tool error';
|
|
33
|
+
const unknown = args.filter((arg) => arg.startsWith('-') && !['--json', '--help'].includes(arg));
|
|
34
|
+
const roots = args.filter((arg) => !arg.startsWith('-'));
|
|
35
|
+
if (unknown.length || roots.length > 1) {
|
|
36
|
+
console.error(usage);
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
} else if (args.includes('--help')) {
|
|
39
|
+
console.log(usage);
|
|
40
|
+
} else {
|
|
41
|
+
const result = inspectVcsRepoFlagPositional(path.resolve(roots[0] ?? process.cwd()));
|
|
42
|
+
if (args.includes('--json')) console.log(JSON.stringify(result, null, 2));
|
|
43
|
+
else {
|
|
44
|
+
console.log('--- Check: --repo/-R requiring a PR positional (WARN-only) ---');
|
|
45
|
+
for (const finding of result.findings) {
|
|
46
|
+
console.log(
|
|
47
|
+
` ${result.toolError ? 'FAIL' : 'WARN'}: [${finding.kind}] ${finding.file}:${finding.line} — ${finding.message}`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (!result.toolError)
|
|
51
|
+
console.log(
|
|
52
|
+
` PASS: scanned ${result.summary.filesScanned} file(s), ${result.findings.length} rejected repo flag(s), ${result.summary.unresolvedArgv} variable argv array(s) unjudged`,
|
|
53
|
+
);
|
|
54
|
+
console.log(`Results: ${result.toolError ? '0 passed, 1 failed' : '1 passed, 0 failed'}`);
|
|
55
|
+
}
|
|
56
|
+
process.exitCode = result.toolError ? 2 : 0;
|
|
57
|
+
}
|
|
58
|
+
}
|