@opengsd/gsd-core 1.12.0 → 1.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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +12 -0
- package/agents/gsd-executor.md +63 -35
- package/agents/gsd-plan-checker.md +76 -57
- package/agents/gsd-planner.md +14 -0
- package/agents/gsd-ui-checker.md +19 -3
- package/agents/gsd-ui-researcher.md +29 -0
- package/agents/gsd-verifier.md +23 -1
- package/bin/install.js +239 -67
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/phase.md +1 -1
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/surface.md +18 -8
- package/gsd-core/bin/gsd-tools.cjs +195 -50
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-registry.cjs +514 -114
- package/gsd-core/bin/lib/capability-state.cjs +7 -1
- package/gsd-core/bin/lib/capability-validator.cjs +120 -4
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +85 -2
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/commands.cjs +337 -13
- package/gsd-core/bin/lib/config-loader.cjs +3 -0
- package/gsd-core/bin/lib/core-utils.cjs +34 -7
- package/gsd-core/bin/lib/decisions.cjs +213 -1
- package/gsd-core/bin/lib/edge-probe.cjs +14 -1
- package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +137 -23
- package/gsd-core/bin/lib/gap-checker.cjs +22 -13
- package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
- package/gsd-core/bin/lib/host-integration.cjs +57 -5
- package/gsd-core/bin/lib/init-command-router.cjs +14 -0
- package/gsd-core/bin/lib/init.cjs +132 -15
- package/gsd-core/bin/lib/install-engine.cjs +184 -12
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
- package/gsd-core/bin/lib/install-profiles.cjs +22 -14
- package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
- package/gsd-core/bin/lib/io.cjs +35 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
- package/gsd-core/bin/lib/markdown-table.cjs +123 -0
- package/gsd-core/bin/lib/milestone.cjs +22 -2
- package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
- package/gsd-core/bin/lib/phase-id.cjs +251 -9
- package/gsd-core/bin/lib/phase.cjs +774 -35
- package/gsd-core/bin/lib/plan-document.cjs +10 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +147 -20
- package/gsd-core/bin/lib/planning-workspace.cjs +103 -28
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
- package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/roadmap-parser.cjs +499 -26
- package/gsd-core/bin/lib/roadmap.cjs +187 -58
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +233 -33
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +286 -108
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +215 -43
- package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
- package/gsd-core/bin/lib/smart-entry.cjs +7 -9
- package/gsd-core/bin/lib/state-document.cjs +30 -5
- package/gsd-core/bin/lib/state-md-schema.cjs +23 -13
- package/gsd-core/bin/lib/state-transition.cjs +333 -44
- package/gsd-core/bin/lib/state.cjs +684 -125
- package/gsd-core/bin/lib/surface.cjs +23 -8
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/uat.cjs +1419 -515
- package/gsd-core/bin/lib/update-context.cjs +6 -2
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +273 -12
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +346 -16
- package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +8 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
- package/gsd-core/references/agent-contracts.md +3 -3
- package/gsd-core/references/edge-probe.md +17 -13
- package/gsd-core/references/execute-mvp-tdd.md +18 -16
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/mvp-concepts.md +2 -2
- package/gsd-core/references/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- package/gsd-core/references/planner-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +75 -2
- package/gsd-core/references/planning-config.md +2 -1
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/tdd.md +14 -9
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/templates/phase-prompt.md +4 -0
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/add-backlog.md +2 -0
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +2 -0
- package/gsd-core/workflows/audit-uat.md +2 -0
- package/gsd-core/workflows/autonomous.md +2 -0
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +15 -13
- package/gsd-core/workflows/code-review-fix.md +2 -0
- package/gsd-core/workflows/code-review.md +73 -31
- package/gsd-core/workflows/complete-milestone.md +13 -4
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +43 -13
- package/gsd-core/workflows/docs-update.md +1 -1
- package/gsd-core/workflows/edit-phase.md +2 -0
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +17 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +32 -14
- package/gsd-core/workflows/execute-plan.md +8 -8
- package/gsd-core/workflows/explore.md +2 -0
- package/gsd-core/workflows/extract-learnings.md +2 -0
- package/gsd-core/workflows/fast.md +6 -0
- package/gsd-core/workflows/forensics.md +2 -0
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +2 -0
- package/gsd-core/workflows/import.md +3 -3
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +2 -0
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +2 -0
- package/gsd-core/workflows/list-workspaces.md +2 -0
- package/gsd-core/workflows/manager.md +3 -3
- package/gsd-core/workflows/map-codebase.md +2 -0
- package/gsd-core/workflows/milestone-summary.md +2 -0
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +1 -1
- package/gsd-core/workflows/new-project.md +5 -3
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -0
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +19 -4
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
- package/gsd-core/workflows/plan-phase.md +130 -12
- package/gsd-core/workflows/plan-review-convergence.md +102 -10
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +11 -3
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +25 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
- package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +13 -3
- package/gsd-core/workflows/reapply-patches.md +2 -0
- package/gsd-core/workflows/remove-phase.md +2 -0
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +6 -2
- package/gsd-core/workflows/review.md +215 -10
- package/gsd-core/workflows/scan.md +2 -0
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +2 -0
- package/gsd-core/workflows/settings-integrations.md +9 -8
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +10 -10
- package/gsd-core/workflows/sketch-wrap-up.md +2 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +24 -19
- package/gsd-core/workflows/spike-wrap-up.md +2 -0
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +2 -0
- package/gsd-core/workflows/sync-skills.md +12 -4
- package/gsd-core/workflows/thread.md +2 -0
- package/gsd-core/workflows/transition.md +2 -0
- package/gsd-core/workflows/ui-phase.md +26 -5
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +2 -0
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/update.md +41 -38
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +49 -3
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-context-monitor.js +283 -12
- package/hooks/dist/gsd-node-runner.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +30 -5
- package/hooks/dist/gsd-read-guard.js +2 -0
- package/hooks/dist/gsd-read-injection-scanner.js +5 -5
- package/hooks/dist/gsd-secret-read-guard.js +1079 -0
- package/hooks/dist/gsd-statusline.js +7 -3
- package/hooks/dist/gsd-validate-commit.sh +444 -7
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-context-monitor.js +283 -12
- package/hooks/gsd-node-runner.sh +1 -0
- package/hooks/gsd-prompt-guard.js +30 -5
- package/hooks/gsd-read-guard.js +2 -0
- package/hooks/gsd-read-injection-scanner.js +5 -5
- package/hooks/gsd-secret-read-guard.js +1079 -0
- package/hooks/gsd-statusline.js +7 -3
- package/hooks/gsd-validate-commit.sh +444 -7
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/hooks.json +6 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +5 -5
- package/scripts/build-hooks.js +11 -4
- package/scripts/ci-test-scope.cjs +7 -0
- package/scripts/docs-guard-registry.cjs +10 -0
- package/scripts/gen-loop-host-contract.cjs +67 -15
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +5 -0
- package/scripts/lint-phase-enumeration-drift.cjs +24 -6
- package/scripts/lint-phase-id-drift.cjs +133 -8
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-response-language-coverage.cjs +524 -0
- package/scripts/lint-test-file-count.allowlist.json +3 -1
- package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +8 -0
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -1
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/vscode/package.json +1 -1
|
@@ -122,6 +122,7 @@ function readEffectiveGitConfig(cwd, deps) {
|
|
|
122
122
|
config = {
|
|
123
123
|
base_branch: _readGitKey(normalized, 'base_branch'),
|
|
124
124
|
protected_branches: _readGitNested(normalized, 'protected_branches'),
|
|
125
|
+
allow_default_branch_commits: _readGitNested(normalized, 'allow_default_branch_commits'),
|
|
125
126
|
};
|
|
126
127
|
}
|
|
127
128
|
}
|
|
@@ -161,7 +162,10 @@ function readEffectiveGitConfig(cwd, deps) {
|
|
|
161
162
|
// Not a list at all — contributes no names, but is still a misconfiguration.
|
|
162
163
|
rejectedProtectedBranches.push(renderRejected(rawProtectedBranches));
|
|
163
164
|
}
|
|
164
|
-
|
|
165
|
+
// #3819: nested-only read (mirrors protected_branches — new key, no legacy
|
|
166
|
+
// flat form, so a top-level spelling must not become an undocumented alias).
|
|
167
|
+
const allowDefaultBranchCommits = config.allow_default_branch_commits === true;
|
|
168
|
+
return { baseBranch, protectedBranches, rejectedProtectedBranches, allowDefaultBranchCommits };
|
|
165
169
|
}
|
|
166
170
|
/**
|
|
167
171
|
* Try `git symbolic-ref --short refs/remotes/origin/HEAD` (no network).
|
|
@@ -302,13 +306,17 @@ function resolveBaseBranch(cwd, deps) {
|
|
|
302
306
|
function resolveProtectedBranchStatus(cwd, currentBranch, deps) {
|
|
303
307
|
const effectiveConfig = readEffectiveGitConfig(cwd, deps);
|
|
304
308
|
const { branch: baseBranch, verified } = resolveBaseBranchDiagnosticsWithConfig(cwd, effectiveConfig.baseBranch, deps);
|
|
305
|
-
const protectedBranches = [...new Set([
|
|
309
|
+
const protectedBranches = [...new Set([
|
|
310
|
+
...(effectiveConfig.allowDefaultBranchCommits ? [] : [baseBranch]),
|
|
311
|
+
...effectiveConfig.protectedBranches,
|
|
312
|
+
])];
|
|
306
313
|
return {
|
|
307
314
|
baseBranch,
|
|
308
315
|
protectedBranches,
|
|
309
316
|
rejectedProtectedBranches: effectiveConfig.rejectedProtectedBranches,
|
|
310
317
|
isProtected: protectedBranches.includes(currentBranch),
|
|
311
318
|
verified,
|
|
319
|
+
allowDefaultBranchCommits: effectiveConfig.allowDefaultBranchCommits,
|
|
312
320
|
};
|
|
313
321
|
}
|
|
314
322
|
/**
|
|
@@ -56,7 +56,13 @@ const healthDiagnosticMod = require("../health-diagnostic-types.cjs");
|
|
|
56
56
|
const { SEVERITY, adviseRemedy } = healthDiagnosticMod;
|
|
57
57
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
58
58
|
const validateMod = require("../validate.cjs");
|
|
59
|
-
|
|
59
|
+
// #612: `isPhaseDirName` is the convention-SELECTED shape test wrapping
|
|
60
|
+
// `phaseDirNameRe`. Handed no convention it delegates to that very regex, so a
|
|
61
|
+
// legacy repo's W005 reading is byte-identical; handed 'bracket' it also
|
|
62
|
+
// recognizes `{CODE}.{MM}-{PP}-slug`, which `phaseDirNameRe` rejects outright —
|
|
63
|
+
// left un-threaded, W005 fires on EVERY phase directory of a repo that opted
|
|
64
|
+
// into the convention, i.e. the check inverts on exactly the repos PR-2 widens.
|
|
65
|
+
const { isPhaseDirName } = validateMod;
|
|
60
66
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
61
67
|
const phaseIdMod = require("../phase-id.cjs");
|
|
62
68
|
const { extractPhaseToken, normalizePhaseName, comparePhaseNum } = phaseIdMod;
|
|
@@ -64,7 +70,7 @@ const { extractPhaseToken, normalizePhaseName, comparePhaseNum } = phaseIdMod;
|
|
|
64
70
|
function checkW005(snapshot) {
|
|
65
71
|
const diagnostics = [];
|
|
66
72
|
for (const name of snapshot.phaseDirs.value) {
|
|
67
|
-
if (!name.
|
|
73
|
+
if (!isPhaseDirName(name, snapshot.phaseIdConvention)) {
|
|
68
74
|
diagnostics.push({
|
|
69
75
|
code: 'W005',
|
|
70
76
|
severity: SEVERITY.WARNING,
|
|
@@ -116,8 +116,16 @@ const { phaseVariants } = validateMod;
|
|
|
116
116
|
* does the roadmap claim, for W007) call this — one matcher, reused, per the
|
|
117
117
|
* file-level comment.
|
|
118
118
|
*/
|
|
119
|
-
function dirsForPhase(dirs, phaseId) {
|
|
120
|
-
|
|
119
|
+
function dirsForPhase(dirs, phaseId, convention) {
|
|
120
|
+
// #612: `convention` reaches BOTH operands of this pairing or it pairs
|
|
121
|
+
// nothing. The phases come from a bracket-aware heading scan and the dirs
|
|
122
|
+
// from a bracket-aware disk scan, so a convention-less selector here leaves
|
|
123
|
+
// W006's existence check and W007's `claimedDirs` empty on exactly the repos
|
|
124
|
+
// both scans just widened — every bracket phase reads as "no directory on
|
|
125
|
+
// disk" AND every bracket directory reads as unclaimed. `matchPhaseDirs`
|
|
126
|
+
// forwards it to its primary `phaseTokenMatches`; handed nothing it is
|
|
127
|
+
// byte-identical, so no legacy repo's pairing changes.
|
|
128
|
+
const canonical = matchPhaseDirs(dirs, normalizePhaseName(phaseId), convention).matches;
|
|
121
129
|
if (canonical.length > 0)
|
|
122
130
|
return canonical;
|
|
123
131
|
// Bug 2 (#3309 W006/W007 migration cluster, found while fixing the
|
|
@@ -139,7 +147,12 @@ function dirsForPhase(dirs, phaseId) {
|
|
|
139
147
|
// alone misses without re-deriving a second matcher.
|
|
140
148
|
const variants = phaseVariants(phaseId);
|
|
141
149
|
return dirs.filter((d) => {
|
|
142
|
-
|
|
150
|
+
// #612: the same convention the primary matcher above got. This fallback
|
|
151
|
+
// compares a DIRECTORY-derived token against phase-id variants, so left
|
|
152
|
+
// convention-less it reads `GSD.02-05-slug` with the legacy grammar and can
|
|
153
|
+
// never agree with a bracket phase id — the fallback would then be dead on
|
|
154
|
+
// precisely the convention it is reached for.
|
|
155
|
+
const token = extractPhaseToken(d, convention).toUpperCase();
|
|
143
156
|
for (const variant of variants) {
|
|
144
157
|
if (token === variant.toUpperCase())
|
|
145
158
|
return true;
|
|
@@ -173,15 +186,40 @@ function checkW006(snapshot) {
|
|
|
173
186
|
if (snapshot.roadmapDeclaredPhases.scope !== SCOPE.COMPLETE)
|
|
174
187
|
return [];
|
|
175
188
|
const dirs = snapshot.allPhaseDirNames.value;
|
|
189
|
+
const convention = snapshot.phaseIdConvention;
|
|
176
190
|
const archivedTokens = new Set(snapshot.archivedPhaseTokens.value);
|
|
191
|
+
// No separate scope guard: `roadmapSentinelPhaseTokens` and
|
|
192
|
+
// `roadmapDeclaredPhases` are produced by ONE builder call
|
|
193
|
+
// (`buildRoadmapDeclaredPhasesField`) from ONE `buildRoadmapPhaseVariants`
|
|
194
|
+
// result, so their scopes are yoked by construction — the COMPLETE check
|
|
195
|
+
// above covers both. Stated rather than left implicit, because reading the
|
|
196
|
+
// sentinel set unconditionally would be a latent bug the day the two fields
|
|
197
|
+
// acquire independent builders: an UNREADABLE sentinel set silently degrades
|
|
198
|
+
// to "nothing is a sentinel," which ADDS W006 warnings rather than dropping
|
|
199
|
+
// them.
|
|
200
|
+
const sentinelTokens = new Set(snapshot.roadmapSentinelPhaseTokens.value);
|
|
177
201
|
const checkboxes = snapshot.roadmapPhaseCheckboxes.value;
|
|
178
202
|
const diagnostics = [];
|
|
179
203
|
for (const { phaseId } of snapshot.roadmapDeclaredPhases.value) {
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
|
|
204
|
+
// A backlog/icebox phase legitimately has no directory. The two guards are
|
|
205
|
+
// DISJOINT, which is why both are needed and neither subsumes the other:
|
|
206
|
+
//
|
|
207
|
+
// #612 `roadmapSentinelPhaseTokens` — bracket sentinels. Under the
|
|
208
|
+
// bracket convention the sentinel-ness lives in the MILESTONE, not
|
|
209
|
+
// the token: the heading `### [GSD.999] 07:` yields phase id `07`,
|
|
210
|
+
// so a bare-token test cannot see it. The snapshot's
|
|
211
|
+
// `buildRoadmapPhaseVariants` call carries the bracket id alongside
|
|
212
|
+
// and reports the token here. Empty unless the bracket convention
|
|
213
|
+
// is active, so no legacy repo's reading changes.
|
|
214
|
+
// #3225 `isSentinelPhaseId` — the legacy/bare leading-int rule (999.x/0.x),
|
|
215
|
+
// which sees `### Phase 999:` and is blind to `[GSD.999] 07`.
|
|
216
|
+
//
|
|
217
|
+
// Upstream's call is kept one-arg verbatim: every `phaseId` here is a bare
|
|
218
|
+
// token, never a `{CODE}.{MM}`-qualified id, so a convention argument could
|
|
219
|
+
// not change its result.
|
|
220
|
+
if (sentinelTokens.has(phaseId) || isSentinelPhaseId(phaseId))
|
|
183
221
|
continue;
|
|
184
|
-
if (dirsForPhase(dirs, phaseId).length > 0)
|
|
222
|
+
if (dirsForPhase(dirs, phaseId, convention).length > 0)
|
|
185
223
|
continue;
|
|
186
224
|
// Bug 1 (#3309 W006/W007 migration cluster): a phase whose ONLY
|
|
187
225
|
// directory lives under a milestone archive
|
|
@@ -209,10 +247,10 @@ function checkW006(snapshot) {
|
|
|
209
247
|
// ─── W007 — an on-disk phase directory has no matching ROADMAP entry ──────
|
|
210
248
|
// (verify.cts:2086-2101)
|
|
211
249
|
/** Every directory in `dirs` that ANY declared roadmap phase resolves to. */
|
|
212
|
-
function computeClaimedDirs(dirs, declaredPhases) {
|
|
250
|
+
function computeClaimedDirs(dirs, declaredPhases, convention) {
|
|
213
251
|
const claimed = new Set();
|
|
214
252
|
for (const { phaseId } of declaredPhases) {
|
|
215
|
-
for (const dir of dirsForPhase(dirs, phaseId))
|
|
253
|
+
for (const dir of dirsForPhase(dirs, phaseId, convention))
|
|
216
254
|
claimed.add(dir);
|
|
217
255
|
}
|
|
218
256
|
return claimed;
|
|
@@ -222,14 +260,19 @@ function checkW007(snapshot) {
|
|
|
222
260
|
if (snapshot.roadmapDeclaredPhases.scope !== SCOPE.COMPLETE)
|
|
223
261
|
return [];
|
|
224
262
|
const dirs = snapshot.allPhaseDirNames.value;
|
|
225
|
-
const
|
|
263
|
+
const convention = snapshot.phaseIdConvention;
|
|
264
|
+
const claimedDirs = computeClaimedDirs(dirs, snapshot.roadmapDeclaredPhases.value, convention);
|
|
226
265
|
const diagnostics = [];
|
|
227
266
|
for (const dirName of dirs) {
|
|
228
267
|
// `extractPhaseToken` is the phase-id.cts owner `PHASE_TOKEN_FROM_DIR_RE`
|
|
229
268
|
// (`src/validate.cts:73-76`) is documented to match exactly
|
|
230
269
|
// (verify.cts's original `p` key from `collectDiskPhaseEntries`,
|
|
231
270
|
// `verify.cts:1373-1397`) — same token, relocated read, not reinvented.
|
|
232
|
-
|
|
271
|
+
// #612: `collectDiskPhaseEntries` keyed that map through the
|
|
272
|
+
// convention-aware extractor, so the token this message names must be
|
|
273
|
+
// derived the same way — otherwise a bracket repo's unclaimed
|
|
274
|
+
// `GSD.02-05-slug` is reported as phase `GSD.02` rather than `05`.
|
|
275
|
+
const token = extractPhaseToken(dirName, convention);
|
|
233
276
|
// #3225: a sentinel dir on disk (999-interim, 0-drafts) is defined as
|
|
234
277
|
// never-on-roadmap; it must not trigger W007. #3639: judged on the DIR
|
|
235
278
|
// NAME via the dir-aware recognizer — the extracted token is
|
|
@@ -111,7 +111,13 @@ const RULE_W024 = {
|
|
|
111
111
|
function buildValidPhaseSet(snapshot) {
|
|
112
112
|
const valid = new Set();
|
|
113
113
|
for (const dir of snapshot.phaseDirs.value) {
|
|
114
|
-
|
|
114
|
+
// #612: the pre-migration source of this third was
|
|
115
|
+
// `collectDiskPhases(planBase, convention)`, whose keys came from the
|
|
116
|
+
// convention-aware extractor. Left convention-less here, a bracket repo's
|
|
117
|
+
// `GSD.02-05-slug` contributes `GSD.02` instead of `05`, so every STATE.md
|
|
118
|
+
// reference to a real phase falls outside the valid set and W002 fires on
|
|
119
|
+
// all of them.
|
|
120
|
+
const token = extractPhaseToken(dir, snapshot.phaseIdConvention);
|
|
115
121
|
if (token)
|
|
116
122
|
valid.add(token);
|
|
117
123
|
}
|
|
@@ -233,31 +239,71 @@ const RULE_W021 = {
|
|
|
233
239
|
description: "Phase's integer prefix implies a different milestone than its ROADMAP section (phase_id_convention: milestone-prefixed)",
|
|
234
240
|
repairable: false,
|
|
235
241
|
check: (snapshot) => {
|
|
236
|
-
const convention = snapshot.config.value?.['phase_id_convention'];
|
|
237
|
-
if (convention !== 'milestone-prefixed')
|
|
238
|
-
return [];
|
|
239
242
|
const diagnostics = [];
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
243
|
+
// Two INDEPENDENT gates, evaluated in the order the pre-migration
|
|
244
|
+
// `cmdValidateHealth` block emitted them (milestone-prefixed first, then
|
|
245
|
+
// bracket). They are deliberately NOT written as if/else: the two read
|
|
246
|
+
// DIFFERENT config bases and can both be true at once.
|
|
247
|
+
//
|
|
248
|
+
// milestone-prefixed — `snapshot.config` (ROOT config only), exactly as
|
|
249
|
+
// this gate has always read it. Federating THIS one silently moved a
|
|
250
|
+
// legacy convention's answer in BOTH directions on workstream repos: a
|
|
251
|
+
// W021 that fires at base vanishing, and one silent at base firing.
|
|
252
|
+
// bracket — `snapshot.phaseIdConvention` (federated workstream -> root),
|
|
253
|
+
// because the bracket reads this half reports on are themselves
|
|
254
|
+
// federated; gating it on the root answer would split the check against
|
|
255
|
+
// the reads it describes.
|
|
256
|
+
//
|
|
257
|
+
// A workstream that overrides a `milestone-prefixed` ROOT to `bracket` is
|
|
258
|
+
// the case that makes the distinction observable, and it is the reason an
|
|
259
|
+
// early `return` here would be a bug: the root-configured M-NN gate must
|
|
260
|
+
// still fire, or the repo looks healthier than it is.
|
|
261
|
+
const rootConvention = snapshot.config.value?.['phase_id_convention'];
|
|
262
|
+
if (rootConvention === 'milestone-prefixed') {
|
|
263
|
+
diagnostics.push(...checkMilestonePrefixW021(snapshot));
|
|
264
|
+
}
|
|
265
|
+
// #612: an ADDITIVE sibling branch — the milestone-prefixed gate above is
|
|
266
|
+
// untouched. Gated strictly on the ACTIVE convention, never on
|
|
267
|
+
// project_code presence: reads are selected per convention, and a CHECK
|
|
268
|
+
// that can fail a repo runs only for the convention that repo opted into.
|
|
269
|
+
if (snapshot.phaseIdConvention === 'bracket') {
|
|
270
|
+
for (const mm of snapshot.roadmapBracketIncoherences.value) {
|
|
271
|
+
diagnostics.push({
|
|
272
|
+
code: 'W021',
|
|
273
|
+
severity: SEVERITY.WARNING,
|
|
274
|
+
message: mm.kind === 'missing-bracket'
|
|
275
|
+
? `Phase ${mm.phaseId}: heading is not in bracket form under the 'bracket' convention (expected \`[CODE.${mm.sectionMilestone}] ${mm.phaseId}:\`)`
|
|
276
|
+
: `Phase ${mm.phaseId}: bracket milestone ${mm.phaseMilestone} does not match its section milestone ${mm.sectionMilestone}`,
|
|
277
|
+
remedy: adviseRemedy('Bracket migration lands with the #612 migrator (PR-3); until then, manually align the bracket milestone in this heading to match the enclosing section.'),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
257
280
|
}
|
|
258
281
|
return diagnostics;
|
|
259
282
|
},
|
|
260
283
|
};
|
|
284
|
+
/** The milestone-prefixed half of W021, unchanged by #612. */
|
|
285
|
+
function checkMilestonePrefixW021(snapshot) {
|
|
286
|
+
const diagnostics = [];
|
|
287
|
+
for (const entry of snapshot.roadmapDeclaredPhases.value) {
|
|
288
|
+
// `entry.milestone === null` means the builder never found this phase
|
|
289
|
+
// heading inside any versioned (`v\d+\.\d+`) section — the original
|
|
290
|
+
// `checkMilestonePrefixMismatches` only ever iterates phases found
|
|
291
|
+
// WITHIN a section, so a phase outside any section is equivalently
|
|
292
|
+
// never checked here.
|
|
293
|
+
if (entry.milestone === null)
|
|
294
|
+
continue;
|
|
295
|
+
const expectedMilestone = getMilestoneFromPhaseId(entry.phaseId);
|
|
296
|
+
if (expectedMilestone === null || expectedMilestone === entry.milestone)
|
|
297
|
+
continue;
|
|
298
|
+
diagnostics.push({
|
|
299
|
+
code: 'W021',
|
|
300
|
+
severity: SEVERITY.WARNING,
|
|
301
|
+
message: `Phase ${entry.phaseId}: integer prefix implies ${expectedMilestone} but listed under ${entry.milestone}`,
|
|
302
|
+
remedy: adviseRemedy('gsd-tools roadmap upgrade --convention milestone-prefixed'),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
return diagnostics;
|
|
306
|
+
}
|
|
261
307
|
// ─── W026 — STATE says milestone complete but ROADMAP lists unstarted phase ─
|
|
262
308
|
const RULE_W026 = {
|
|
263
309
|
code: 'W026',
|
|
@@ -282,7 +328,14 @@ const RULE_W026 = {
|
|
|
282
328
|
// unwindowed `phaseDirNames2` (`verify.cts:2372-2382`, a direct
|
|
283
329
|
// `readdirSync` of the phases dir), not the current-milestone-windowed
|
|
284
330
|
// `phaseDirs`.
|
|
285
|
-
|
|
331
|
+
// #612: the directory read widens with the heading read that produced
|
|
332
|
+
// `currentMilestoneRoadmapPhaseIds`, or every bracket phase resolves to
|
|
333
|
+
// nothing, reads as unstarted, and this UNGATED warning false-fires on
|
|
334
|
+
// any bracket repo whose STATE says the milestone is complete. #2528
|
|
335
|
+
// moved the read onto the shared selector; the convention rides along,
|
|
336
|
+
// since the selector's primary match IS the `phaseTokenMatches` call this
|
|
337
|
+
// line used to make directly.
|
|
338
|
+
const hasDirectory = matchPhaseDirs(snapshot.allPhaseDirNames.value, normalized, snapshot.phaseIdConvention).matches.length > 0;
|
|
286
339
|
if (!hasDirectory)
|
|
287
340
|
unstarted.push(phaseId);
|
|
288
341
|
}
|
|
@@ -72,7 +72,7 @@ const INTERFACE_POINTS = Object.freeze(['command', 'dispatch', 'model', 'hooks',
|
|
|
72
72
|
const SAFE_DEFAULTS = {
|
|
73
73
|
embeddingMode: 'declarative',
|
|
74
74
|
commandSurface: 'prose-only',
|
|
75
|
-
dispatch: { namedDispatch: false, nested: false, maxDepth: 0, background: false, subagentToolkit: 'read-only', backgroundDispatch: false, isolation: 'none' },
|
|
75
|
+
dispatch: { namedDispatch: false, nested: false, maxDepth: 0, background: false, subagentToolkit: 'read-only', backgroundDispatch: false, isolation: 'none', maxConcurrency: 1 },
|
|
76
76
|
modelMode: 'passive',
|
|
77
77
|
hookBus: 'none',
|
|
78
78
|
stateIO: 'session-log-append',
|
|
@@ -84,7 +84,7 @@ const PROFILE_BASELINES = Object.freeze({
|
|
|
84
84
|
'programmatic-cli': Object.freeze({
|
|
85
85
|
embeddingMode: 'imperative',
|
|
86
86
|
commandSurface: 'slash-file',
|
|
87
|
-
dispatch: Object.freeze({ namedDispatch: true, nested: true, maxDepth: -1, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none' }),
|
|
87
|
+
dispatch: Object.freeze({ namedDispatch: true, nested: true, maxDepth: -1, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none', maxConcurrency: 1 }),
|
|
88
88
|
modelMode: 'passive',
|
|
89
89
|
hookBus: 'host',
|
|
90
90
|
stateIO: 'filesystem',
|
|
@@ -95,7 +95,7 @@ const PROFILE_BASELINES = Object.freeze({
|
|
|
95
95
|
'declarative-cli': Object.freeze({
|
|
96
96
|
embeddingMode: 'declarative',
|
|
97
97
|
commandSurface: 'slash-file',
|
|
98
|
-
dispatch: Object.freeze({ namedDispatch: true, nested: false, maxDepth: 1, background: false, subagentToolkit: 'full', backgroundDispatch: false, isolation: 'none' }),
|
|
98
|
+
dispatch: Object.freeze({ namedDispatch: true, nested: false, maxDepth: 1, background: false, subagentToolkit: 'full', backgroundDispatch: false, isolation: 'none', maxConcurrency: 1 }),
|
|
99
99
|
modelMode: 'passive',
|
|
100
100
|
hookBus: 'host',
|
|
101
101
|
stateIO: 'filesystem',
|
|
@@ -106,7 +106,7 @@ const PROFILE_BASELINES = Object.freeze({
|
|
|
106
106
|
'ide': Object.freeze({
|
|
107
107
|
embeddingMode: 'imperative',
|
|
108
108
|
commandSurface: 'palette',
|
|
109
|
-
dispatch: Object.freeze({ namedDispatch: true, nested: true, maxDepth: 5, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none' }),
|
|
109
|
+
dispatch: Object.freeze({ namedDispatch: true, nested: true, maxDepth: 5, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none', maxConcurrency: 1 }),
|
|
110
110
|
modelMode: 'active',
|
|
111
111
|
hookBus: 'engine',
|
|
112
112
|
stateIO: 'sandboxed-storage',
|
|
@@ -235,7 +235,7 @@ const DEFAULT_ENGINE = {
|
|
|
235
235
|
axes: {
|
|
236
236
|
embeddingMode: 'imperative',
|
|
237
237
|
commandSurface: 'slash-file',
|
|
238
|
-
dispatch: { namedDispatch: true, nested: true, maxDepth: -1, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none' },
|
|
238
|
+
dispatch: { namedDispatch: true, nested: true, maxDepth: -1, background: true, subagentToolkit: 'full', backgroundDispatch: true, isolation: 'none', maxConcurrency: 1 },
|
|
239
239
|
modelMode: 'active',
|
|
240
240
|
hookBus: 'host',
|
|
241
241
|
stateIO: 'filesystem',
|
|
@@ -246,6 +246,19 @@ const DEFAULT_ENGINE = {
|
|
|
246
246
|
known: HOST_INTEGRATION_AXES,
|
|
247
247
|
};
|
|
248
248
|
// ---------------------------------------------------------------------------
|
|
249
|
+
// isPositiveSafeInteger — shared predicate (#3673)
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
/**
|
|
252
|
+
* True iff `v` is a positive (>0) JS safe integer. Single source of truth for
|
|
253
|
+
* the dispatch.maxConcurrency contract: used by negotiateHostCapabilities
|
|
254
|
+
* below, and by gsd-core/bin/gsd-tools.cjs's routeDispatchCapacity (which
|
|
255
|
+
* requires this module's compiled output rather than reimplementing the
|
|
256
|
+
* predicate), so the two consumers cannot silently diverge.
|
|
257
|
+
*/
|
|
258
|
+
function isPositiveSafeInteger(v) {
|
|
259
|
+
return typeof v === 'number' && Number.isSafeInteger(v) && v > 0;
|
|
260
|
+
}
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
249
262
|
// negotiateHostCapabilities
|
|
250
263
|
// ---------------------------------------------------------------------------
|
|
251
264
|
/**
|
|
@@ -335,6 +348,7 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
335
348
|
let effectiveSubagentToolkit;
|
|
336
349
|
let effectiveMaxDepth;
|
|
337
350
|
let effectiveIsolation;
|
|
351
|
+
let effectiveMaxConcurrency;
|
|
338
352
|
if (hostDispatch === null) {
|
|
339
353
|
// Host didn't declare dispatch at all — fail-closed to most-restrictive values
|
|
340
354
|
warnings.push(`host did not declare 'dispatch'`);
|
|
@@ -345,6 +359,7 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
345
359
|
effectiveSubagentToolkit = 'read-only';
|
|
346
360
|
effectiveMaxDepth = 0;
|
|
347
361
|
effectiveIsolation = 'none';
|
|
362
|
+
effectiveMaxConcurrency = 1;
|
|
348
363
|
}
|
|
349
364
|
else {
|
|
350
365
|
// N1: observability warnings for 'undocumented' sentinel on dispatch fields
|
|
@@ -408,6 +423,41 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
408
423
|
const eDepthNum = engineDispatch.maxDepth < 0 ? Infinity : engineDispatch.maxDepth;
|
|
409
424
|
const minDepth = Math.min(hDepthNum, eDepthNum);
|
|
410
425
|
effectiveMaxDepth = minDepth === Infinity ? -1 : minDepth;
|
|
426
|
+
// maxConcurrency (#3673, ADR-1239 Phase 1): a positive-safe-integer
|
|
427
|
+
// passthrough with a fail-closed floor of 1. UNLIKE maxDepth, there is no
|
|
428
|
+
// min(host, engine) reduction here — the design doc explicitly rejects an
|
|
429
|
+
// engine-side ceiling for this field (no competing intrinsic worker
|
|
430
|
+
// ceiling to cap against at the negotiation layer); a `--jobs N` cap is
|
|
431
|
+
// applied later, at the Phase 4 scheduler.
|
|
432
|
+
const hostMaxConcurrency = hostDispatch.maxConcurrency;
|
|
433
|
+
if (hostMaxConcurrency === UNDOCUMENTED) {
|
|
434
|
+
warnings.push(`dispatch.maxConcurrency is undocumented — degraded closed (1)`);
|
|
435
|
+
effectiveMaxConcurrency = 1;
|
|
436
|
+
}
|
|
437
|
+
else if (isPositiveSafeInteger(hostMaxConcurrency)) {
|
|
438
|
+
effectiveMaxConcurrency = hostMaxConcurrency;
|
|
439
|
+
}
|
|
440
|
+
else if (hostMaxConcurrency === undefined) {
|
|
441
|
+
warnings.push(`host did not declare 'dispatch.maxConcurrency' — treating as 1`);
|
|
442
|
+
effectiveMaxConcurrency = 1;
|
|
443
|
+
}
|
|
444
|
+
else if (typeof hostMaxConcurrency !== 'number') {
|
|
445
|
+
warnings.push(`host dispatch.maxConcurrency is not a number — treating as 1`);
|
|
446
|
+
effectiveMaxConcurrency = 1;
|
|
447
|
+
}
|
|
448
|
+
else if (!Number.isSafeInteger(hostMaxConcurrency)) {
|
|
449
|
+
if (Number.isInteger(hostMaxConcurrency)) {
|
|
450
|
+
warnings.push(`host dispatch.maxConcurrency is an unsafe integer — treating as 1`);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
warnings.push(`host dispatch.maxConcurrency is not an integer — treating as 1`);
|
|
454
|
+
}
|
|
455
|
+
effectiveMaxConcurrency = 1;
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
warnings.push(`host dispatch.maxConcurrency is non-positive — treating as 1`);
|
|
459
|
+
effectiveMaxConcurrency = 1;
|
|
460
|
+
}
|
|
411
461
|
// If namedDispatch is false, cap maxDepth/nested/background/backgroundDispatch to 0/false/false/false (struct consistency)
|
|
412
462
|
if (!effectiveNamedDispatch) {
|
|
413
463
|
effectiveMaxDepth = 0;
|
|
@@ -424,6 +474,7 @@ function negotiateHostCapabilities(host, engine = DEFAULT_ENGINE) {
|
|
|
424
474
|
subagentToolkit: effectiveSubagentToolkit,
|
|
425
475
|
backgroundDispatch: effectiveBackgroundDispatch,
|
|
426
476
|
isolation: effectiveIsolation,
|
|
477
|
+
maxConcurrency: effectiveMaxConcurrency,
|
|
427
478
|
};
|
|
428
479
|
// ---------------------------------------------------------------------------
|
|
429
480
|
// Assemble effective axes
|
|
@@ -753,6 +804,7 @@ module.exports = {
|
|
|
753
804
|
EXTENSION_EVENT_SURFACES,
|
|
754
805
|
degradationFor,
|
|
755
806
|
profileOf,
|
|
807
|
+
isPositiveSafeInteger,
|
|
756
808
|
negotiateHostCapabilities,
|
|
757
809
|
shouldFlattenDispatch,
|
|
758
810
|
resolveDispatchType,
|
|
@@ -158,6 +158,20 @@ function routeInitCommand({ init, args, cwd, raw, error }) {
|
|
|
158
158
|
full: namedArgs['full'],
|
|
159
159
|
});
|
|
160
160
|
},
|
|
161
|
+
// #3676 (Phase 4, epic #3344): `init.quick-batch` supplies model
|
|
162
|
+
// profiles/commit_docs/roadmap-existence/section_manifest — batch
|
|
163
|
+
// creation itself is the `quick-batch create` verb's job (wraps
|
|
164
|
+
// `createBatch`, src/quick-batch.cts). No free-text description to
|
|
165
|
+
// strip: `--research`/`--validate` are the only recognized flags
|
|
166
|
+
// (`--discuss`/`--full` are rejected upstream by `parseQuickBatchArgs`
|
|
167
|
+
// before this init bundle is ever reached).
|
|
168
|
+
'quick-batch': () => {
|
|
169
|
+
const namedArgs = (0, command_arg_projection_cjs_1.parseNamedArgsOrExit)(args, { booleanFlags: ['research', 'validate'], positionals: 2 }, error);
|
|
170
|
+
init.cmdInitQuickBatch(cwd, raw, {
|
|
171
|
+
research: namedArgs['research'],
|
|
172
|
+
validate: namedArgs['validate'],
|
|
173
|
+
});
|
|
174
|
+
},
|
|
161
175
|
'ingest-docs': () => init.cmdInitIngestDocs(cwd, raw),
|
|
162
176
|
resume: () => init.cmdInitResume(cwd, raw),
|
|
163
177
|
// ADR-3473 §8.4 / #3358 gap: these handlers read args[2] positionally
|