@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
|
@@ -165,7 +165,7 @@ function normalizeSkillManifest(runtimeConfigDir, manifest) {
|
|
|
165
165
|
* - registry is threaded into resolveProfile so capability skills
|
|
166
166
|
* participate in the base skill set and their requires: chains expand.
|
|
167
167
|
*/
|
|
168
|
-
function resolveSurface(runtimeConfigDir, manifest, clusterMap, registry) {
|
|
168
|
+
function resolveSurface(runtimeConfigDir, manifest, clusterMap, registry, surfaceOverride) {
|
|
169
169
|
// Merge capability clusters into the cluster map when registry is provided.
|
|
170
170
|
// The ADR-857 phase 4a HARD gate guarantees that when a capId matches a CLUSTERS
|
|
171
171
|
// key, the values are EQUAL — so the spread is idempotent for matching names.
|
|
@@ -205,7 +205,7 @@ function resolveSurface(runtimeConfigDir, manifest, clusterMap, registry) {
|
|
|
205
205
|
cm = merged;
|
|
206
206
|
}
|
|
207
207
|
const skillManifest = normalizeSkillManifest(runtimeConfigDir, manifest);
|
|
208
|
-
const surface = readSurface(runtimeConfigDir);
|
|
208
|
+
const surface = surfaceOverride === undefined ? readSurface(runtimeConfigDir) : surfaceOverride;
|
|
209
209
|
// Determine base profile name: from surface state or from .gsd-profile marker
|
|
210
210
|
const baseProfileName = (surface && surface.baseProfile)
|
|
211
211
|
? surface.baseProfile
|
|
@@ -316,10 +316,9 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap, registry,
|
|
|
316
316
|
os: deps.os, env: deps.env,
|
|
317
317
|
});
|
|
318
318
|
const skillManifest = normalizeSkillManifest(layout.configDir, manifest);
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
retiredArtifactCleanup.pruneRetiredRuntimeArtifacts(layout.runtime, layout.configDir);
|
|
319
|
+
const hasCandidateState = opts !== undefined && Object.prototype.hasOwnProperty.call(opts, 'surfaceState');
|
|
320
|
+
const candidateState = hasCandidateState ? (opts.surfaceState ?? null) : undefined;
|
|
321
|
+
const resolved = resolveSurface(layout.configDir, skillManifest, clusterMap, registry, candidateState);
|
|
323
322
|
// #1575: agents kind now mirrors createRuntimeArtifactInstallPlan — build
|
|
324
323
|
// agentCtx (pathPrefix + attribution) and pass it to kind.stage() so
|
|
325
324
|
// stageAgentsForRuntimeWithConverter applies the full inline-loop pipeline
|
|
@@ -353,7 +352,7 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap, registry,
|
|
|
353
352
|
// not referenced by any skill's _calls_agents_ manifest entry would be silently
|
|
354
353
|
// dropped from the surface path. For tiered profiles (core/standard) or when
|
|
355
354
|
// surface mods exist, pass the resolved set so only the filtered subset stages.
|
|
356
|
-
const _surfaceState = readSurface(layout.configDir);
|
|
355
|
+
const _surfaceState = candidateState === undefined ? readSurface(layout.configDir) : candidateState;
|
|
357
356
|
const _baseProfileName = (_surfaceState && _surfaceState.baseProfile)
|
|
358
357
|
? _surfaceState.baseProfile
|
|
359
358
|
: (readActiveProfile(layout.configDir) || 'full');
|
|
@@ -361,6 +360,7 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap, registry,
|
|
|
361
360
|
_surfaceState.explicitAdds.length > 0 ||
|
|
362
361
|
_surfaceState.explicitRemoves.length > 0);
|
|
363
362
|
const _isUnmodifiedFull = _baseProfileName === 'full' && !_hasSurfaceMods;
|
|
363
|
+
const stagedKinds = [];
|
|
364
364
|
try {
|
|
365
365
|
for (const kind of layout.kinds) {
|
|
366
366
|
let staged;
|
|
@@ -396,7 +396,22 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap, registry,
|
|
|
396
396
|
// the parity test in tests/runtime-artifact-layout-surface.test.cjs
|
|
397
397
|
// enforces that the two writers never diverge again.
|
|
398
398
|
const dest = assertDestWithinConfigHome(kind.home ?? layout.configDir, kind.destSubpath);
|
|
399
|
-
|
|
399
|
+
stagedKinds.push({ kind, staged, dest });
|
|
400
|
+
}
|
|
401
|
+
// Do not mutate installed artifacts until every kind has staged
|
|
402
|
+
// successfully. A missing source provider therefore leaves both artifacts
|
|
403
|
+
// and the persisted surface state untouched.
|
|
404
|
+
retiredArtifactCleanup.pruneRetiredRuntimeArtifacts(layout.runtime, layout.configDir);
|
|
405
|
+
for (const item of stagedKinds) {
|
|
406
|
+
_syncGsdDir(item.staged, item.dest, item.kind, skillManifest, layout.runtime);
|
|
407
|
+
}
|
|
408
|
+
if (hasCandidateState) {
|
|
409
|
+
if (candidateState === null) {
|
|
410
|
+
node_fs_1.default.rmSync(node_path_1.default.join(runtimeConfigDir, SURFACE_FILE_NAME), { force: true });
|
|
411
|
+
}
|
|
412
|
+
else if (candidateState !== undefined) {
|
|
413
|
+
writeSurface(runtimeConfigDir, candidateState);
|
|
414
|
+
}
|
|
400
415
|
}
|
|
401
416
|
}
|
|
402
417
|
finally {
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TDD RED-evidence classification (#3770).
|
|
4
|
+
*
|
|
5
|
+
* The `type: tdd` executor gate previously accepted ANY nonzero test command as
|
|
6
|
+
* RED: syntax errors, zero-test discovery, fixture crashes, parser errors, and
|
|
7
|
+
* unrelated assertions all authorized production edits (GREEN). This module
|
|
8
|
+
* defines the compact RED evidence the gate now requires — the TARGET test's
|
|
9
|
+
* identity plus a matching assertion failure — and classifies a persisted test
|
|
10
|
+
* run into exactly one verdict:
|
|
11
|
+
*
|
|
12
|
+
* RED_EVIDENCE_OK — nonzero exit AND the target test failed as a REAL test
|
|
13
|
+
* (distinctly named, TAP-reported failure). The ONLY
|
|
14
|
+
* verdict that may advance to GREEN.
|
|
15
|
+
* INVALID_RED — everything else, with a machine-readable reason:
|
|
16
|
+
* unexpected_green | zero_tests_discovered |
|
|
17
|
+
* nonzero_exit_without_test_failure |
|
|
18
|
+
* fixture_or_load_failure | no_target_test_failure |
|
|
19
|
+
* invalid_record | unreadable_record (router arm).
|
|
20
|
+
*
|
|
21
|
+
* Everything here is PURE — no fs, no spawn, no clock — so the executor can
|
|
22
|
+
* persist the record (command, exit code, failing test, expected, actual) and
|
|
23
|
+
* validate it via `gsd_run check tdd-red-evidence <record.json>`.
|
|
24
|
+
*
|
|
25
|
+
* TAP parsing reuses the proven primitives from prohibition-enforcement
|
|
26
|
+
* (`parseNodeTestSummary`, `tapFailedTestNames`) — the same contract the
|
|
27
|
+
* prohibition probe's fail-first prover already relies on (#1259).
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.classifyRedEvidence = classifyRedEvidence;
|
|
31
|
+
exports.buildRedEvidenceRecord = buildRedEvidenceRecord;
|
|
32
|
+
const prohibition_enforcement_cjs_1 = require("./prohibition-enforcement.cjs");
|
|
33
|
+
/** Basename of a path-like string ('' for non-strings) — separators `/` and `\`. */
|
|
34
|
+
function baseOf(p) {
|
|
35
|
+
return typeof p === 'string' ? (p.split(/[\\/]/).pop() ?? p) : '';
|
|
36
|
+
}
|
|
37
|
+
/** Coerce and validate the raw record's scalar fields. Returns null exit_code only when absent/non-numeric. */
|
|
38
|
+
function readInput(input) {
|
|
39
|
+
const command = typeof input?.command === 'string' ? input.command : '';
|
|
40
|
+
const output = typeof input?.output === 'string' ? input.output : '';
|
|
41
|
+
const targetTest = typeof input?.targetTest === 'string' ? input.targetTest.trim() : '';
|
|
42
|
+
const exitCode = typeof input?.exitCode === 'number' && Number.isFinite(input.exitCode) ? input.exitCode : null;
|
|
43
|
+
if (!command || !targetTest || exitCode === null)
|
|
44
|
+
return null;
|
|
45
|
+
return { command, exitCode, output, targetTest };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Classify a persisted RED-phase test run. Fail-closed: malformed input, an
|
|
49
|
+
* unparseable/empty TAP summary, a file-named (load/crash) failure, or a
|
|
50
|
+
* failure that is not the target test's are all INVALID_RED — only a nonzero
|
|
51
|
+
* exit WITH the distinctly-named target test failing is RED_EVIDENCE_OK.
|
|
52
|
+
* Never throws.
|
|
53
|
+
*/
|
|
54
|
+
function classifyRedEvidence(input) {
|
|
55
|
+
const parsed = readInput(input);
|
|
56
|
+
if (!parsed) {
|
|
57
|
+
return {
|
|
58
|
+
verdict: 'INVALID_RED',
|
|
59
|
+
reason: 'invalid_record',
|
|
60
|
+
evidence: {
|
|
61
|
+
command: typeof input?.command === 'string' ? input.command : '',
|
|
62
|
+
exit_code: null,
|
|
63
|
+
target_test: '',
|
|
64
|
+
tests: 0,
|
|
65
|
+
pass: 0,
|
|
66
|
+
fail: 0,
|
|
67
|
+
failing_tests: [],
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const { command, exitCode, output, targetTest } = parsed;
|
|
72
|
+
const summary = (0, prohibition_enforcement_cjs_1.parseNodeTestSummary)(output);
|
|
73
|
+
const failing = (0, prohibition_enforcement_cjs_1.tapFailedTestNames)(output);
|
|
74
|
+
const evidence = {
|
|
75
|
+
command,
|
|
76
|
+
exit_code: exitCode,
|
|
77
|
+
target_test: targetTest,
|
|
78
|
+
tests: summary.tests,
|
|
79
|
+
pass: summary.pass,
|
|
80
|
+
fail: summary.fail,
|
|
81
|
+
failing_tests: failing,
|
|
82
|
+
};
|
|
83
|
+
// Existing fail-fast rule, now machine-checked: exit 0 during RED is an
|
|
84
|
+
// unexpected GREEN — the feature may already exist or the test is wrong.
|
|
85
|
+
if (exitCode === 0) {
|
|
86
|
+
return { verdict: 'INVALID_RED', reason: 'unexpected_green', evidence };
|
|
87
|
+
}
|
|
88
|
+
// Zero-test discovery: the discovery pattern / fixture matched no tests.
|
|
89
|
+
// A run that executed nothing cannot prove anything about the behavior.
|
|
90
|
+
if (summary.tests === 0) {
|
|
91
|
+
return { verdict: 'INVALID_RED', reason: 'zero_tests_discovered', evidence };
|
|
92
|
+
}
|
|
93
|
+
// Nonzero exit but TAP reports no failing test: harness/setup/parser crash
|
|
94
|
+
// whose failure never reached a test assertion (or unparseable output).
|
|
95
|
+
if (summary.fail === 0 || failing.length === 0) {
|
|
96
|
+
return { verdict: 'INVALID_RED', reason: 'nonzero_exit_without_test_failure', evidence };
|
|
97
|
+
}
|
|
98
|
+
// Fixture/load failure: every failing entry is named like the target FILE —
|
|
99
|
+
// node reports a load-time crash (throw-on-require, syntax error, ENOENT
|
|
100
|
+
// fixture) as a file-named `not ok 1 - <file>`, never the target test.
|
|
101
|
+
const targetBase = baseOf(input?.targetFile ?? '');
|
|
102
|
+
const distinctlyNamed = failing.filter((n) => (targetBase ? baseOf(n) !== targetBase : true));
|
|
103
|
+
if (distinctlyNamed.length === 0) {
|
|
104
|
+
return { verdict: 'INVALID_RED', reason: 'fixture_or_load_failure', evidence };
|
|
105
|
+
}
|
|
106
|
+
// Unrelated failure: real tests ran and failed, but none is the target test
|
|
107
|
+
// the plan named — an unrelated assertion must not authorize GREEN.
|
|
108
|
+
if (!distinctlyNamed.includes(targetTest)) {
|
|
109
|
+
return { verdict: 'INVALID_RED', reason: 'no_target_test_failure', evidence };
|
|
110
|
+
}
|
|
111
|
+
return { verdict: 'RED_EVIDENCE_OK', reason: 'target_test_failed', evidence };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Project a classification into the persisted record shape — command, exit
|
|
115
|
+
* code, failing test, expected, actual, verdict, reason — so the evidence
|
|
116
|
+
* survives past the terminal and the gate can re-verify it deterministically.
|
|
117
|
+
* Pure: JSON-serializable, no timestamps (the record's mtime/commit carries time).
|
|
118
|
+
*/
|
|
119
|
+
function buildRedEvidenceRecord(input, result) {
|
|
120
|
+
const failingTest = result.evidence.failing_tests.find((n) => n === result.evidence.target_test) ??
|
|
121
|
+
result.evidence.failing_tests[0] ??
|
|
122
|
+
null;
|
|
123
|
+
return {
|
|
124
|
+
command: result.evidence.command,
|
|
125
|
+
exit_code: result.evidence.exit_code,
|
|
126
|
+
failing_test: failingTest,
|
|
127
|
+
target_test: result.evidence.target_test,
|
|
128
|
+
expected: typeof input?.expected === 'string' ? input.expected : null,
|
|
129
|
+
actual: typeof input?.actual === 'string' ? input.actual : null,
|
|
130
|
+
verdict: result.verdict,
|
|
131
|
+
reason: result.reason,
|
|
132
|
+
};
|
|
133
|
+
}
|