@opengsd/gsd-core 1.13.0 → 1.15.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/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +459 -0
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +360 -0
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +13 -8
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +8 -7
- package/agents/gsd-planner.md +12 -8
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-auditor.md +155 -17
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +10 -9
- package/bin/install.js +848 -163
- package/commands/gsd/autonomous.md +2 -2
- package/commands/gsd/capture.md +1 -1
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +8 -3
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +1 -0
- package/commands/gsd/plan-review-convergence.md +6 -6
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/progress.md +1 -1
- package/commands/gsd/quick-batch.md +1 -1
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/review.md +2 -3
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +672 -146
- package/gsd-core/bin/lib/adr-parser.cjs +4 -2
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +119 -34
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +96 -189
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-validator.cjs +14 -2
- package/gsd-core/bin/lib/check-command-router.cjs +213 -49
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
- package/gsd-core/bin/lib/commands.cjs +823 -112
- package/gsd-core/bin/lib/config-loader.cjs +66 -4
- package/gsd-core/bin/lib/config.cjs +186 -45
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +164 -45
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/frontmatter.cjs +13 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +614 -86
- package/gsd-core/bin/lib/install-engine.cjs +29 -3
- package/gsd-core/bin/lib/install-profiles.cjs +14 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
- package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +37 -13
- package/gsd-core/bin/lib/model-resolver.cjs +253 -53
- package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +268 -27
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase.cjs +393 -88
- package/gsd-core/bin/lib/plan-document.cjs +49 -1
- package/gsd-core/bin/lib/planning-document.cjs +459 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
- package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
- package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch.cjs +1 -1
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
- package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
- package/gsd-core/bin/lib/roadmap.cjs +356 -42
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
- package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
- package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
- package/gsd-core/bin/lib/state-document.cjs +130 -28
- package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
- package/gsd-core/bin/lib/state-transition.cjs +181 -30
- package/gsd-core/bin/lib/state.cjs +265 -27
- package/gsd-core/bin/lib/surface.cjs +77 -3
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
- package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
- package/gsd-core/bin/lib/uat.cjs +9 -1
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/update-context.cjs +30 -24
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification.cjs +315 -30
- package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
- package/gsd-core/bin/lib/verify.cjs +320 -48
- package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
- package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-phase-between-wave-reset.md +7 -6
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +13 -4
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +14 -2
- package/gsd-core/references/tdd.md +30 -4
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/worktree-path-safety.md +433 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +1 -1
- package/gsd-core/workflows/add-tests.md +2 -2
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +11 -3
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +29 -16
- package/gsd-core/workflows/check-todos.md +6 -4
- package/gsd-core/workflows/cleanup.md +5 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
- package/gsd-core/workflows/code-review-fix.md +108 -22
- package/gsd-core/workflows/code-review.md +216 -73
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +41 -264
- package/gsd-core/workflows/debug.md +3 -3
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +17 -158
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +10 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +83 -172
- package/gsd-core/workflows/execute-plan.md +24 -10
- package/gsd-core/workflows/explore.md +3 -3
- package/gsd-core/workflows/extract-learnings.md +2 -1
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +2 -2
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/help.md +1 -1
- package/gsd-core/workflows/import.md +2 -2
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +2 -2
- package/gsd-core/workflows/map-codebase.md +52 -5
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +56 -14
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +39 -209
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +45 -187
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +132 -20
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +1 -1
- package/gsd-core/workflows/quick.md +29 -10
- package/gsd-core/workflows/reapply-patches.md +86 -6
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +31 -16
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -2
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +22 -9
- package/gsd-core/workflows/ship.md +3 -2
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +44 -8
- package/gsd-core/workflows/ui-review.md +18 -4
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +14 -12
- package/gsd-core/workflows/validate-phase.md +3 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +101 -196
- package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
- package/hooks/dist/gsd-context-monitor.js +88 -15
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-secret-read-guard.js +71 -19
- package/hooks/dist/gsd-statusline.js +81 -20
- package/hooks/dist/gsd-validate-commit.sh +97 -8
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/gsd-agent-isolation-guard.js +66 -16
- package/hooks/gsd-context-monitor.js +88 -15
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-secret-read-guard.js +71 -19
- package/hooks/gsd-statusline.js +81 -20
- package/hooks/gsd-validate-commit.sh +97 -8
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/package.json +11 -6
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +15 -6
- package/scripts/check-contract-drift.cjs +131 -12
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +126 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/command-contract-helpers.cjs +3 -0
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +35 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +69 -0
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
- package/scripts/lib/ndjson-reporter.cjs +3 -2
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-id-drift.cjs +623 -13
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +9 -3
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +29 -9
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
- package/scripts/prompt-injection-scan.sh +18 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-autonomous/SKILL.md +2 -2
- package/skills/gsd-capture/SKILL.md +1 -1
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +8 -3
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -0
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-progress/SKILL.md +1 -1
- package/skills/gsd-quick-batch/SKILL.md +1 -1
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review/SKILL.md +2 -3
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"ui_phase": true,
|
|
39
39
|
"ui_safety_gate": true,
|
|
40
40
|
"text_mode": false,
|
|
41
|
+
"compact_content": false,
|
|
41
42
|
"research_before_questions": false,
|
|
42
43
|
"discuss_mode": "discuss",
|
|
43
44
|
"skip_discuss": false,
|
|
@@ -67,6 +68,9 @@
|
|
|
67
68
|
"sub_repos": [],
|
|
68
69
|
"granularity": "standard"
|
|
69
70
|
},
|
|
71
|
+
"planner": {
|
|
72
|
+
"stall_detection_enabled": true
|
|
73
|
+
},
|
|
70
74
|
"hooks": {
|
|
71
75
|
"context_warnings": true,
|
|
72
76
|
"workflow_guard": false
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"workflow.smart_zone_tokens",
|
|
23
23
|
"workflow.human_verify_mode",
|
|
24
24
|
"workflow.text_mode",
|
|
25
|
+
"workflow.compact_content",
|
|
25
26
|
"workflow.research_before_questions",
|
|
26
27
|
"workflow.discuss_mode",
|
|
27
28
|
"workflow.skip_discuss",
|
|
@@ -71,11 +72,16 @@
|
|
|
71
72
|
"workflow.context_guard_mode",
|
|
72
73
|
"executor.stall_detect_interval_minutes",
|
|
73
74
|
"executor.stall_threshold_minutes",
|
|
75
|
+
"planner.stall_detection_enabled",
|
|
74
76
|
"planner.stall_detect_interval_minutes",
|
|
75
77
|
"planner.stall_threshold_minutes",
|
|
76
78
|
"workflow.inline_plan_threshold",
|
|
77
79
|
"hooks.context_warnings",
|
|
80
|
+
"hooks.context_warning_threshold",
|
|
81
|
+
"hooks.context_critical_threshold",
|
|
78
82
|
"hooks.workflow_guard",
|
|
83
|
+
"hooks.commit_types",
|
|
84
|
+
"hooks.community",
|
|
79
85
|
"workflow.context_coverage_gate",
|
|
80
86
|
"statusline.show_last_command",
|
|
81
87
|
"statusline.context_position",
|
|
@@ -17,37 +17,75 @@
|
|
|
17
17
|
* # false-positive halts beat silent successes
|
|
18
18
|
* # on lost content)
|
|
19
19
|
* [--json] # emit JSON report instead of human text
|
|
20
|
+
* [--classify] # pre-merge mode: classify each backed-up
|
|
21
|
+
* # file as incorporated / needs_merge /
|
|
22
|
+
* # unknown (#4136); always exits 0
|
|
23
|
+
* [--min-baseline-coverage <0..1>] # OPT-IN strict gate (#4135): exit 3
|
|
24
|
+
* # when the fraction of files verified
|
|
25
|
+
* # against a resolved pristine baseline
|
|
26
|
+
* # (baseline_covered / checked) falls below
|
|
27
|
+
* # the threshold. Default: off — a
|
|
28
|
+
* # low-coverage run stays green per the
|
|
29
|
+
* # documented #934 advisory posture, but
|
|
30
|
+
* # its coverage is ALWAYS headline-reported.
|
|
20
31
|
*
|
|
21
|
-
* Exit codes:
|
|
32
|
+
* Exit codes (default gate mode):
|
|
22
33
|
* 0 — every user-added line is present in the merged file (gate passes)
|
|
23
|
-
* 1 — at least one missing line in at least one file (gate fails)
|
|
34
|
+
* 1 — at least one missing line in at least one file (gate fails); outranks
|
|
35
|
+
* a coverage-gate failure when both apply
|
|
24
36
|
* 2 — usage / structural error (e.g. patches dir missing)
|
|
37
|
+
* 3 — opt-in coverage gate failed (--min-baseline-coverage not met; #4135)
|
|
25
38
|
*
|
|
26
39
|
* Bug #2969: the Step 5 gate previously trusted Claude's free-text "verified:
|
|
27
40
|
* yes/no" reporting per hunk. The LLM was filling in `yes` even when content
|
|
28
41
|
* had been silently dropped. Moving the check to a deterministic script is the
|
|
29
42
|
* durability fix.
|
|
43
|
+
*
|
|
44
|
+
* Bug #4136 adds --classify (pre-merge mode, always exit 0 — informational;
|
|
45
|
+
* the binding gate remains the post-merge default run): per backed-up file,
|
|
46
|
+
* decides whether the user's modification was already adopted upstream
|
|
47
|
+
* ("Incorporated", reapply-patches.md Step 4 item 6). The classification is
|
|
48
|
+
* ONLY produced from a hash-validated pristine baseline with every
|
|
49
|
+
* significant user-added line present verbatim in the freshly installed
|
|
50
|
+
* version — a false Incorporated silently retires a live customization,
|
|
51
|
+
* which is worse than no Incorporated at all.
|
|
30
52
|
*/
|
|
31
53
|
|
|
32
54
|
const fs = require('node:fs');
|
|
33
55
|
const path = require('node:path');
|
|
34
56
|
const crypto = require('node:crypto');
|
|
35
57
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
58
|
+
// #4145: shared hash-first recovery for baselines stored at an unexpected
|
|
59
|
+
// path under gsd-pristine/ (e.g. without the gsd-core/ prefix an earlier
|
|
60
|
+
// release's writer dropped). Same module the installer's preserve-check uses,
|
|
61
|
+
// so the two readers cannot drift apart again.
|
|
62
|
+
const { findPristineByHash, findPristineInGit } = require('./lib/pristine-baseline.cjs');
|
|
36
63
|
|
|
37
64
|
const SIGNIFICANT_MIN_CHARS = 12;
|
|
38
65
|
const GSD_HOOK_VERSION_LINE_RE = /^(?:\/\/|#)\s*gsd-hook-version:\s*\S+\s*$/i;
|
|
39
66
|
|
|
40
67
|
function parseArgs(argv) {
|
|
41
|
-
const opts = { patchesDir: null, configDir: null, pristineDir: null, json: false };
|
|
68
|
+
const opts = { patchesDir: null, configDir: null, pristineDir: null, json: false, classify: false, minBaselineCoverage: null };
|
|
42
69
|
for (let i = 0; i < argv.length; i++) {
|
|
43
70
|
const arg = argv[i];
|
|
44
71
|
if (arg === '--patches-dir') opts.patchesDir = argv[++i];
|
|
45
72
|
else if (arg === '--config-dir') opts.configDir = argv[++i];
|
|
46
73
|
else if (arg === '--pristine-dir') opts.pristineDir = argv[++i];
|
|
47
74
|
else if (arg === '--json') opts.json = true;
|
|
48
|
-
else if (arg === '--
|
|
75
|
+
else if (arg === '--classify') opts.classify = true;
|
|
76
|
+
else if (arg === '--min-baseline-coverage') {
|
|
77
|
+
const raw = argv[++i];
|
|
78
|
+
if (raw === undefined) {
|
|
79
|
+
throw new ExitError(2, '--min-baseline-coverage requires a value between 0 and 1');
|
|
80
|
+
}
|
|
81
|
+
const value = Number(raw);
|
|
82
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
83
|
+
throw new ExitError(2, `--min-baseline-coverage must be a number between 0 and 1, got: ${raw}`);
|
|
84
|
+
}
|
|
85
|
+
opts.minBaselineCoverage = value;
|
|
86
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
49
87
|
process.stdout.write(
|
|
50
|
-
'usage: verify-reapply-patches.cjs --patches-dir <path> --config-dir <path> [--pristine-dir <path>] [--json]\n',
|
|
88
|
+
'usage: verify-reapply-patches.cjs --patches-dir <path> --config-dir <path> [--pristine-dir <path>] [--json] [--classify] [--min-baseline-coverage <0..1>]\n',
|
|
51
89
|
);
|
|
52
90
|
throw new ExitError(0);
|
|
53
91
|
} else {
|
|
@@ -178,12 +216,155 @@ const REASON = Object.freeze({
|
|
|
178
216
|
// block" rather than "ignore everything" — it only applies when the hash was
|
|
179
217
|
// recorded (modern installer) but the file is absent (specific gap).
|
|
180
218
|
OK_NO_BASELINE: 'ok_no_baseline',
|
|
219
|
+
// Bug #4136: an on-disk gsd-pristine/ snapshot exists for this file but
|
|
220
|
+
// backup-meta.json records no pristine_hashes entry for it (older
|
|
221
|
+
// installer), so nothing confirms the snapshot is the baseline the backup
|
|
222
|
+
// was captured against — it could be a drifted newer-version snapshot the
|
|
223
|
+
// #3657 guard cannot see. The default gate still uses it as the diff
|
|
224
|
+
// baseline (pre-#3657 behaviour, unchanged); --classify refuses to confirm
|
|
225
|
+
// adoption on an unvalidated baseline, so a file is never classified
|
|
226
|
+
// Incorporated on the snapshot's say-so alone.
|
|
227
|
+
OK_UNVALIDATED_BASELINE: 'ok_unvalidated_baseline',
|
|
181
228
|
FAIL_INSTALLED_MISSING: 'fail_installed_missing',
|
|
182
229
|
FAIL_INSTALLED_NOT_REGULAR_FILE: 'fail_installed_not_regular_file',
|
|
183
230
|
FAIL_READ_ERROR: 'fail_read_error',
|
|
184
231
|
FAIL_USER_LINES_MISSING: 'fail_user_lines_missing',
|
|
185
232
|
});
|
|
186
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Bug #4136: stable per-file classification codes for --classify (pre-merge)
|
|
236
|
+
* mode. Tests assert via `assert.equal(result.classification, CLASSIFICATION.X)`
|
|
237
|
+
* rather than regex-matching prose, mirroring the REASON enum contract.
|
|
238
|
+
*
|
|
239
|
+
* INCORPORATED — hash-validated pristine baseline confirms that every
|
|
240
|
+
* significant user-added line is already present verbatim
|
|
241
|
+
* in the freshly installed version: upstream adopted the
|
|
242
|
+
* customization. The workflow must NOT re-apply the diff.
|
|
243
|
+
* NEEDS_MERGE — validated baseline, but at least one significant
|
|
244
|
+
* user-added line is absent from the fresh install; the
|
|
245
|
+
* missing lines are listed for the merge step.
|
|
246
|
+
* UNKNOWN — no confirmable baseline (drift #3657, absent #934,
|
|
247
|
+
* unvalidated, or no --pristine-dir), zero significant
|
|
248
|
+
* user-added delta, or a structural failure. Never
|
|
249
|
+
* Incorporated.
|
|
250
|
+
*/
|
|
251
|
+
const CLASSIFICATION = Object.freeze({
|
|
252
|
+
INCORPORATED: 'incorporated',
|
|
253
|
+
NEEDS_MERGE: 'needs_merge',
|
|
254
|
+
UNKNOWN: 'unknown',
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Typed outcome of resolving one file's pristine baseline. Shared by the
|
|
259
|
+
* post-merge gate (verifyFile) and the pre-merge classifier (classifyFile)
|
|
260
|
+
* so the two can never drift on what counts as a usable baseline (#4136).
|
|
261
|
+
*/
|
|
262
|
+
const PRISTINE_RESOLUTION = Object.freeze({
|
|
263
|
+
VALIDATED: 'validated', // recorded hash matches the resolved snapshot
|
|
264
|
+
UNVALIDATED: 'unvalidated', // snapshot present, no recorded hash to confirm it
|
|
265
|
+
DRIFTED: 'drifted', // recorded hash mismatches the snapshot (#3657)
|
|
266
|
+
ABSENT_RECORDED: 'absent_recorded', // recorded hash, no snapshot anywhere (#934/#4145)
|
|
267
|
+
OVERBROAD: 'overbroad', // no pristine dir, or a non-file at the path
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Resolve the pristine baseline for one backed-up file, applying the #3657
|
|
272
|
+
* drift guard, the #4145 hash-first recovery, and the #934 absent-baseline
|
|
273
|
+
* guard. Extracted from verifyFile's inline block so --classify reasons over
|
|
274
|
+
* the exact same baseline semantics the post-merge gate enforces.
|
|
275
|
+
*/
|
|
276
|
+
function resolvePristineBaseline({ relPath, configDir, pristineDir, pristineHashes }) {
|
|
277
|
+
const hashKey = relPath.replace(/\\/g, '/');
|
|
278
|
+
const recordedHash = pristineHashes && pristineHashes[hashKey];
|
|
279
|
+
if (pristineDir) {
|
|
280
|
+
const pristinePath = path.join(pristineDir, relPath);
|
|
281
|
+
let pristinePathExists = false;
|
|
282
|
+
try {
|
|
283
|
+
const stat = fs.statSync(pristinePath);
|
|
284
|
+
pristinePathExists = true; // path exists (any type)
|
|
285
|
+
if (stat.isFile()) {
|
|
286
|
+
const candidate = fs.readFileSync(pristinePath, 'utf8');
|
|
287
|
+
if (recordedHash) {
|
|
288
|
+
if (sha256(candidate) === recordedHash) {
|
|
289
|
+
// Hash matches: the on-disk pristine is the correct baseline.
|
|
290
|
+
return { resolution: PRISTINE_RESOLUTION.VALIDATED, content: candidate };
|
|
291
|
+
}
|
|
292
|
+
// Hash mismatch: the on-disk gsd-pristine/ was refreshed to a newer
|
|
293
|
+
// GSD version after the backup was captured (Bug #3657). Using it as
|
|
294
|
+
// the diff baseline would invert the delta and produce false
|
|
295
|
+
// FAIL_USER_LINES_MISSING reports.
|
|
296
|
+
return { resolution: PRISTINE_RESOLUTION.DRIFTED, content: null };
|
|
297
|
+
}
|
|
298
|
+
// No recorded hash for this file (older installer or absent
|
|
299
|
+
// backup-meta) — the default gate uses the on-disk pristine as-is
|
|
300
|
+
// (pre-fix behaviour); --classify treats it as unvalidated.
|
|
301
|
+
return { resolution: PRISTINE_RESOLUTION.UNVALIDATED, content: candidate };
|
|
302
|
+
}
|
|
303
|
+
// Non-file at pristinePath (e.g. a directory): fall through to
|
|
304
|
+
// over-broad mode below, which is safe and conservative.
|
|
305
|
+
} catch {
|
|
306
|
+
// Pristine stat threw — path is absent (ENOENT) or inaccessible.
|
|
307
|
+
// pristinePathExists stays false.
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Bug #4145: the canonical join missed, but the recorded hash is the
|
|
311
|
+
// baseline authority the #3657 drift guard already trusts. Before
|
|
312
|
+
// reporting ABSENT_RECORDED, scan gsd-pristine/ for byte-identical content
|
|
313
|
+
// (an earlier release may have stored the snapshot without the gsd-core/
|
|
314
|
+
// prefix). An exact sha-256 match cannot be the wrong baseline, and
|
|
315
|
+
// gsd-pristine/ holds only backed-up files, so the scan is small. The
|
|
316
|
+
// canonical path itself is excluded — a mismatching file at the joined
|
|
317
|
+
// path is drift (#3657), never re-adopted through the scan. A recovered
|
|
318
|
+
// baseline is hash-confirmed by construction, so it VALIDATES.
|
|
319
|
+
if (!pristinePathExists && recordedHash) {
|
|
320
|
+
try {
|
|
321
|
+
const recoveredRel = findPristineByHash(pristineDir, recordedHash, hashKey);
|
|
322
|
+
if (recoveredRel) {
|
|
323
|
+
return {
|
|
324
|
+
resolution: PRISTINE_RESOLUTION.VALIDATED,
|
|
325
|
+
content: fs.readFileSync(path.join(pristineDir, recoveredRel), 'utf8'),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
// scan or read failure — fall through to the ABSENT_RECORDED posture
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (pristinePathExists) {
|
|
334
|
+
// Present but not a regular file — over-broad mode is the safe side.
|
|
335
|
+
return { resolution: PRISTINE_RESOLUTION.OVERBROAD, content: null };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Bug #4135: still nothing under gsd-pristine/ — the multi-version
|
|
339
|
+
// regeneration collapse (the #3407 promotion rule only keeps files
|
|
340
|
+
// byte-identical across the WHOLE version span, so the surviving set is
|
|
341
|
+
// precisely the files upstream did not change). When the config dir is
|
|
342
|
+
// itself a git repository, its history may hold the outgoing bytes: this
|
|
343
|
+
// is the workflow's documented Option A (reapply-patches.md Step 2),
|
|
344
|
+
// anchored by the SAME authority every other tier trusts — exact
|
|
345
|
+
// pristine_hashes equality. Read-only (git log / git show); any failure
|
|
346
|
+
// (no git, not a repo, no matching blob) degrades to ABSENT_RECORDED.
|
|
347
|
+
// A recovered baseline is hash-confirmed by construction, so it VALIDATES.
|
|
348
|
+
if (!pristinePathExists && recordedHash) {
|
|
349
|
+
try {
|
|
350
|
+
const fromGit = findPristineInGit(configDir, hashKey, recordedHash);
|
|
351
|
+
if (fromGit !== null) {
|
|
352
|
+
return { resolution: PRISTINE_RESOLUTION.VALIDATED, content: fromGit };
|
|
353
|
+
}
|
|
354
|
+
} catch {
|
|
355
|
+
// git unavailable or history walk failed — ABSENT_RECORDED posture
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Bug #934: recordedHash is present (modern installer) but no
|
|
359
|
+
// hash-matching pristine exists anywhere under gsd-pristine/ (the stat
|
|
360
|
+
// missed and the #4145 recovery found nothing).
|
|
361
|
+
if (recordedHash) {
|
|
362
|
+
return { resolution: PRISTINE_RESOLUTION.ABSENT_RECORDED, content: null };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return { resolution: PRISTINE_RESOLUTION.OVERBROAD, content: null };
|
|
366
|
+
}
|
|
367
|
+
|
|
187
368
|
/**
|
|
188
369
|
* #4086: resolve where a backed-up file's INSTALLED counterpart lives.
|
|
189
370
|
* Primary is the config-dir-relative join (the manifest key's native form).
|
|
@@ -225,7 +406,7 @@ function resolveSkillsRedirect(configDir) {
|
|
|
225
406
|
if (!kind) return null;
|
|
226
407
|
const root = path.resolve(path.join(kind.home || configDir, kind.destSubpath));
|
|
227
408
|
const resolvedConfig = path.resolve(configDir);
|
|
228
|
-
if (root === resolvedConfig || root.startsWith(resolvedConfig + path.sep)) return null;
|
|
409
|
+
if (root === resolvedConfig || root.startsWith(resolvedConfig + path.sep)) return null; // allow-handrolled-containment: answers whether the skills root is a separate location, not a containment/admission decision
|
|
229
410
|
// Same descriptor-driven manifest prefix writeManifest uses for skills
|
|
230
411
|
// keys (hermes nests under 'skills/gsd/', everyone else 'skills/'), read
|
|
231
412
|
// from the same shipped registry the installer's _resolveHostBehaviors
|
|
@@ -247,6 +428,47 @@ function resolveSkillsRedirect(configDir) {
|
|
|
247
428
|
}
|
|
248
429
|
}
|
|
249
430
|
|
|
431
|
+
/**
|
|
432
|
+
* #4135: baseline-coverage accounting. A file counts as baseline-covered
|
|
433
|
+
* when its diff was actually computed against a resolved pristine baseline,
|
|
434
|
+
* i.e. its reason is one of the baseline-diff outcomes (null = lines
|
|
435
|
+
* verified present, OK_NO_USER_LINES_VS_PRISTINE = no user lines versus
|
|
436
|
+
* the baseline, FAIL_USER_LINES_MISSING = lines missing). Everything else
|
|
437
|
+
* is uncovered: the silent skips (no baseline anywhere per #934/#4135,
|
|
438
|
+
* drift per #3657, no-pristine over-broad with nothing significant) AND
|
|
439
|
+
* the structural failures (installed missing / not a file / read error),
|
|
440
|
+
* which never reached a baseline — they are loud blocking failures, but
|
|
441
|
+
* they were not baseline-verified either, and this aggregate exists to
|
|
442
|
+
* state exactly how much of the run the gate could reason about. When
|
|
443
|
+
* --pristine-dir is absent the whole run is over-broad (#2998 fallback):
|
|
444
|
+
* no file was verified against a baseline, covered is 0 by construction.
|
|
445
|
+
*/
|
|
446
|
+
const BASELINE_VERIFIED_REASONS = new Set([
|
|
447
|
+
REASON.OK_NO_USER_LINES_VS_PRISTINE,
|
|
448
|
+
REASON.FAIL_USER_LINES_MISSING,
|
|
449
|
+
]);
|
|
450
|
+
|
|
451
|
+
function classifyBaselineCoverage(results, pristineDirProvided) {
|
|
452
|
+
if (!pristineDirProvided) return 0;
|
|
453
|
+
let covered = 0;
|
|
454
|
+
for (const r of results) {
|
|
455
|
+
if (r.reason === null || BASELINE_VERIFIED_REASONS.has(r.reason)) covered++;
|
|
456
|
+
}
|
|
457
|
+
return covered;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* #4135: the headline string for the human summary. A run with 12 of 13
|
|
462
|
+
* files unbaselined must not present the same way as a fully-verified one —
|
|
463
|
+
* the N-of-M form is the issue's own framing, and the unverified tail is
|
|
464
|
+
* only rendered when it is non-zero.
|
|
465
|
+
*/
|
|
466
|
+
function coverageHeadline(covered, total) {
|
|
467
|
+
const unverified = Math.max(0, total - covered);
|
|
468
|
+
const base = `Baseline coverage: ${covered} of ${total} file(s) verified against a pristine baseline`;
|
|
469
|
+
return unverified > 0 ? `${base} (${unverified} unverified)` : base;
|
|
470
|
+
}
|
|
471
|
+
|
|
250
472
|
function verifyFile({ relPath, patchesDir, configDir, pristineDir, pristineHashes, skillsRedirect }) {
|
|
251
473
|
const backupPath = path.join(patchesDir, relPath);
|
|
252
474
|
const installedPath = resolveInstalledPath(configDir, relPath, skillsRedirect);
|
|
@@ -287,80 +509,32 @@ function verifyFile({ relPath, patchesDir, configDir, pristineDir, pristineHashe
|
|
|
287
509
|
// Normalize to forward slashes so the key lookup matches on Windows
|
|
288
510
|
// where path.join produces backslash-separated relPath values but
|
|
289
511
|
// backup-meta.json stores keys written with forward slashes.
|
|
290
|
-
|
|
291
|
-
|
|
512
|
+
// #4136: the inline baseline block (stat/read + #3657 drift guard + #4145
|
|
513
|
+
// hash-first recovery + #934 absent guard) moved into resolvePristineBaseline
|
|
514
|
+
// so the pre-merge classifier reasons over the exact same semantics.
|
|
515
|
+
const { resolution, content: pristineContent } =
|
|
516
|
+
resolvePristineBaseline({ relPath, configDir, pristineDir, pristineHashes });
|
|
292
517
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
// OK_NO_BASELINE is reserved for the strictly absent case: stat throws,
|
|
301
|
-
// meaning the file was never written (the gap the bug describes).
|
|
302
|
-
let pristinePathExists = false;
|
|
303
|
-
try {
|
|
304
|
-
const stat = fs.statSync(pristinePath);
|
|
305
|
-
pristinePathExists = true; // path exists (any type)
|
|
306
|
-
if (stat.isFile()) {
|
|
307
|
-
const candidate = fs.readFileSync(pristinePath, 'utf8');
|
|
308
|
-
// Bug #3657: if backup-meta.json recorded a pristine_hash for this
|
|
309
|
-
// file, validate that the on-disk pristine matches it. A mismatch
|
|
310
|
-
// means the installer refreshed gsd-pristine/ to a newer GSD version
|
|
311
|
-
// after the backup was captured. Using the wrong-version pristine as
|
|
312
|
-
// the diff baseline inverts the delta: upstream removals appear as
|
|
313
|
-
// "user-added lines that must survive", causing FAIL_USER_LINES_MISSING
|
|
314
|
-
// false positives. When the hash is stale, skip the pristine and fall
|
|
315
|
-
// through to over-broad mode (every significant backup line is checked).
|
|
316
|
-
// Over-broad mode never false-fails for a different reason because all
|
|
317
|
-
// backup lines that are genuinely user-added will still be present in a
|
|
318
|
-
// correctly merged install.
|
|
319
|
-
if (recordedHash) {
|
|
320
|
-
if (sha256(candidate) === recordedHash) {
|
|
321
|
-
// Hash matches: the on-disk pristine is the correct baseline.
|
|
322
|
-
pristineContent = candidate;
|
|
323
|
-
} else {
|
|
324
|
-
// Hash mismatch: the on-disk gsd-pristine/ was refreshed to a newer
|
|
325
|
-
// GSD version after the backup was captured. Using it as the diff
|
|
326
|
-
// baseline would invert the delta and produce false FAIL_USER_LINES_MISSING
|
|
327
|
-
// reports (Bug #3657). Report the file as ok with a diagnostic code
|
|
328
|
-
// so the gate does not false-fail; a re-anchor or git-aware baseline
|
|
329
|
-
// step is required to verify this file correctly.
|
|
330
|
-
result.reason = REASON.OK_PRISTINE_DRIFT_DETECTED;
|
|
331
|
-
return result;
|
|
332
|
-
}
|
|
333
|
-
} else {
|
|
334
|
-
// No recorded hash for this file (older installer or absent
|
|
335
|
-
// backup-meta) — use the on-disk pristine as-is (pre-fix behaviour).
|
|
336
|
-
pristineContent = candidate;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
// Non-file at pristinePath (e.g. a directory): stat succeeded so
|
|
340
|
-
// pristinePathExists is true; we fall through to over-broad mode below,
|
|
341
|
-
// which is safe and conservative.
|
|
342
|
-
} catch {
|
|
343
|
-
// Pristine stat threw — path is absent (ENOENT) or inaccessible.
|
|
344
|
-
// pristinePathExists stays false.
|
|
345
|
-
}
|
|
518
|
+
// Bug #3657: the resolved snapshot hash-mismatches the recorded baseline.
|
|
519
|
+
// Skip the file with a diagnostic code rather than diffing against the
|
|
520
|
+
// wrong baseline (a re-anchor or git-aware baseline step is required).
|
|
521
|
+
if (resolution === PRISTINE_RESOLUTION.DRIFTED) {
|
|
522
|
+
result.reason = REASON.OK_PRISTINE_DRIFT_DETECTED;
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
346
525
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
// false FAIL_USER_LINES_MISSING for each upstream removal. Since we
|
|
354
|
-
// cannot reason correctly without a baseline, the safe answer is advisory/
|
|
355
|
-
// non-blocking: return OK_NO_BASELINE and let the caller decide.
|
|
356
|
-
// NOTE: this guard fires ONLY when stat threw (path absent), not when the
|
|
357
|
-
// path is present but non-file — in that case over-broad mode is safer.
|
|
358
|
-
if (!pristinePathExists && recordedHash) {
|
|
359
|
-
result.reason = REASON.OK_NO_BASELINE;
|
|
360
|
-
return result;
|
|
361
|
-
}
|
|
526
|
+
// Bug #934 / #4145: a hash was recorded (modern installer) but no
|
|
527
|
+
// hash-matching pristine exists anywhere under gsd-pristine/ —
|
|
528
|
+
// advisory/non-blocking, the caller logs a warning.
|
|
529
|
+
if (resolution === PRISTINE_RESOLUTION.ABSENT_RECORDED) {
|
|
530
|
+
result.reason = REASON.OK_NO_BASELINE;
|
|
531
|
+
return result;
|
|
362
532
|
}
|
|
363
533
|
|
|
534
|
+
// VALIDATED / UNVALIDATED keep the gate's pre-#4136 semantics: the resolved
|
|
535
|
+
// content (null for OVERBROAD) feeds computeUserAddedLines, whose no-pristine
|
|
536
|
+
// branch is the over-broad fallback.
|
|
537
|
+
|
|
364
538
|
const userAdded = computeUserAddedLines(backupContent, pristineContent);
|
|
365
539
|
if (userAdded.length === 0) {
|
|
366
540
|
// Backup and pristine match exactly (or no significant content) — nothing
|
|
@@ -383,6 +557,101 @@ function verifyFile({ relPath, patchesDir, configDir, pristineDir, pristineHashe
|
|
|
383
557
|
return result;
|
|
384
558
|
}
|
|
385
559
|
|
|
560
|
+
/**
|
|
561
|
+
* Bug #4136: pre-merge classification for reapply-patches Step 4. Runs over
|
|
562
|
+
* the SAME fixture state the gate sees (patches dir + freshly installed
|
|
563
|
+
* config dir + optional pristine dir) but BEFORE any merge, answering the
|
|
564
|
+
* question the gate cannot: is this file's user modification already adopted
|
|
565
|
+
* upstream, such that the workflow should leave the installed file untouched
|
|
566
|
+
* (status `Incorporated`) instead of re-grafting the diff?
|
|
567
|
+
*
|
|
568
|
+
* The classification is deliberately conservative in the direction the issue
|
|
569
|
+
* demands ("a false Incorporated is worse than no Incorporated"):
|
|
570
|
+
* - only a hash-VALIDATED pristine baseline can confirm adoption — drift
|
|
571
|
+
* (#3657), absent (#934/#4145), unvalidated (no recorded hash), and a
|
|
572
|
+
* missing --pristine-dir all classify UNKNOWN;
|
|
573
|
+
* - every significant user-added line (structural/trivial lines excluded by
|
|
574
|
+
* isSignificantLine) must be present verbatim in the fresh install — a
|
|
575
|
+
* signature-looking short line or a code fence matching anywhere in the
|
|
576
|
+
* new file proves nothing;
|
|
577
|
+
* - a backup with zero significant delta vs the validated pristine is
|
|
578
|
+
* UNKNOWN (the workflow's Critical invariant: a backed-up file is never
|
|
579
|
+
* concluded to have "no custom content", and Incorporated is a positive
|
|
580
|
+
* adoption finding, not a skip);
|
|
581
|
+
* - structural failures are reported with their FAIL_* reason but never
|
|
582
|
+
* gate the run — the binding enforcement point stays the post-merge gate.
|
|
583
|
+
*/
|
|
584
|
+
function classifyFile({ relPath, patchesDir, configDir, pristineDir, pristineHashes, skillsRedirect }) {
|
|
585
|
+
const backupPath = path.join(patchesDir, relPath);
|
|
586
|
+
const installedPath = resolveInstalledPath(configDir, relPath, skillsRedirect);
|
|
587
|
+
const result = { file: relPath, classification: CLASSIFICATION.UNKNOWN, reason: null, missing: [] };
|
|
588
|
+
|
|
589
|
+
if (!fs.existsSync(backupPath) || !fs.statSync(backupPath).isFile()) {
|
|
590
|
+
return result; // walked entry no longer exists — non-fatal
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
let installedStat;
|
|
594
|
+
try {
|
|
595
|
+
installedStat = fs.statSync(installedPath);
|
|
596
|
+
} catch {
|
|
597
|
+
result.reason = REASON.FAIL_INSTALLED_MISSING;
|
|
598
|
+
return result;
|
|
599
|
+
}
|
|
600
|
+
if (!installedStat.isFile()) {
|
|
601
|
+
result.reason = REASON.FAIL_INSTALLED_NOT_REGULAR_FILE;
|
|
602
|
+
return result;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
let backupContent;
|
|
606
|
+
let installedContent;
|
|
607
|
+
try {
|
|
608
|
+
backupContent = fs.readFileSync(backupPath, 'utf8');
|
|
609
|
+
installedContent = fs.readFileSync(installedPath, 'utf8');
|
|
610
|
+
} catch {
|
|
611
|
+
result.reason = REASON.FAIL_READ_ERROR;
|
|
612
|
+
return result;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const { resolution, content: pristineContent } =
|
|
616
|
+
resolvePristineBaseline({ relPath, configDir, pristineDir, pristineHashes });
|
|
617
|
+
|
|
618
|
+
if (resolution === PRISTINE_RESOLUTION.DRIFTED) {
|
|
619
|
+
result.reason = REASON.OK_PRISTINE_DRIFT_DETECTED;
|
|
620
|
+
return result;
|
|
621
|
+
}
|
|
622
|
+
if (resolution === PRISTINE_RESOLUTION.ABSENT_RECORDED) {
|
|
623
|
+
result.reason = REASON.OK_NO_BASELINE;
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
if (resolution === PRISTINE_RESOLUTION.UNVALIDATED) {
|
|
627
|
+
result.reason = REASON.OK_UNVALIDATED_BASELINE;
|
|
628
|
+
return result;
|
|
629
|
+
}
|
|
630
|
+
if (resolution !== PRISTINE_RESOLUTION.VALIDATED) {
|
|
631
|
+
// OVERBROAD: no --pristine-dir (two-way fallback) or a non-file at the
|
|
632
|
+
// pristine path. Without a baseline there is no adoption evidence.
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const userAdded = computeUserAddedLines(backupContent, pristineContent);
|
|
637
|
+
if (userAdded.length === 0) {
|
|
638
|
+
result.reason = REASON.OK_NO_USER_LINES_VS_PRISTINE;
|
|
639
|
+
return result;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
for (const line of userAdded) {
|
|
643
|
+
if (!installedContent.includes(line)) {
|
|
644
|
+
result.missing.push(line.trim());
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (result.missing.length === 0) {
|
|
648
|
+
result.classification = CLASSIFICATION.INCORPORATED;
|
|
649
|
+
} else {
|
|
650
|
+
result.classification = CLASSIFICATION.NEEDS_MERGE;
|
|
651
|
+
}
|
|
652
|
+
return result;
|
|
653
|
+
}
|
|
654
|
+
|
|
386
655
|
function main() {
|
|
387
656
|
const opts = parseArgs(process.argv.slice(2));
|
|
388
657
|
if (!opts.patchesDir || !opts.configDir) {
|
|
@@ -402,6 +671,55 @@ function main() {
|
|
|
402
671
|
// #4086: skills-root fallback for runtimes whose skills kind declares a
|
|
403
672
|
// `home` override outside configDir (codex global → $HOME/.agents/skills).
|
|
404
673
|
const skillsRedirect = resolveSkillsRedirect(opts.configDir);
|
|
674
|
+
|
|
675
|
+
// Bug #4136: --classify is the pre-merge mode the workflow runs in Step 4
|
|
676
|
+
// to decide which files NOT to merge. It is informational — always exit 0 —
|
|
677
|
+
// because the binding enforcement stays with the post-merge gate below;
|
|
678
|
+
// a needs_merge or unknown outcome is direction for the merge step, not a
|
|
679
|
+
// failure, and halting here would block the very merge that resolves it.
|
|
680
|
+
if (opts.classify) {
|
|
681
|
+
const classifyResults = files.map((relPath) =>
|
|
682
|
+
classifyFile({
|
|
683
|
+
relPath,
|
|
684
|
+
patchesDir: opts.patchesDir,
|
|
685
|
+
configDir: opts.configDir,
|
|
686
|
+
pristineDir: opts.pristineDir,
|
|
687
|
+
pristineHashes,
|
|
688
|
+
skillsRedirect,
|
|
689
|
+
}),
|
|
690
|
+
);
|
|
691
|
+
const incorporatedResults = classifyResults.filter(
|
|
692
|
+
(r) => r.classification === CLASSIFICATION.INCORPORATED,
|
|
693
|
+
);
|
|
694
|
+
const incorporated = incorporatedResults.length;
|
|
695
|
+
const incorporated_files = incorporatedResults.map((r) => r.file);
|
|
696
|
+
|
|
697
|
+
if (opts.json) {
|
|
698
|
+
process.stdout.write(
|
|
699
|
+
JSON.stringify({ checked: classifyResults.length, incorporated, incorporated_files, results: classifyResults }, null, 2) + '\n',
|
|
700
|
+
);
|
|
701
|
+
} else {
|
|
702
|
+
process.stdout.write(`# Reapply Patch Classification (#4136)\n\n`);
|
|
703
|
+
process.stdout.write(`Checked: ${classifyResults.length} file(s)\n`);
|
|
704
|
+
process.stdout.write(`Incorporated: ${incorporated} file(s)\n`);
|
|
705
|
+
for (const f of incorporated_files) {
|
|
706
|
+
process.stdout.write(` incorporated: ${f}\n`);
|
|
707
|
+
}
|
|
708
|
+
for (const r of classifyResults) {
|
|
709
|
+
if (r.classification === CLASSIFICATION.NEEDS_MERGE) {
|
|
710
|
+
process.stdout.write(` needs merge: ${r.file}\n`);
|
|
711
|
+
for (const line of r.missing.slice(0, 5)) {
|
|
712
|
+
process.stdout.write(` missing: ${line}\n`);
|
|
713
|
+
}
|
|
714
|
+
if (r.missing.length > 5) {
|
|
715
|
+
process.stdout.write(` …and ${r.missing.length - 5} more line(s)\n`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return 0;
|
|
721
|
+
}
|
|
722
|
+
|
|
405
723
|
const results = files.map((relPath) =>
|
|
406
724
|
verifyFile({
|
|
407
725
|
relPath,
|
|
@@ -433,14 +751,52 @@ function main() {
|
|
|
433
751
|
const no_baseline = noBaselineResults.length;
|
|
434
752
|
const no_baseline_files = noBaselineResults.map((r) => r.file);
|
|
435
753
|
|
|
754
|
+
// Bug #4135: baseline coverage is a first-class aggregate. The collapse
|
|
755
|
+
// this reports is silent by design in every other surface (no_baseline is
|
|
756
|
+
// advisory, exit stays 0), which is exactly why it must be counted here:
|
|
757
|
+
// "A clean verifier exit reads as 'hunks survived'. On this run it meant
|
|
758
|
+
// '12 of 13 files were not checked at all.'"
|
|
759
|
+
const baseline_covered = classifyBaselineCoverage(results, Boolean(opts.pristineDir));
|
|
760
|
+
|
|
761
|
+
// Bug #4135: the opt-in strict coverage gate. Threshold semantics are >=
|
|
762
|
+
// (a run exactly at the threshold passes); an empty run is vacuously
|
|
763
|
+
// covered (nothing checked cannot be under-covered). A content failure
|
|
764
|
+
// (exit 1) outranks a coverage failure — it is the louder, more specific
|
|
765
|
+
// signal.
|
|
766
|
+
let coverageGateFailed = false;
|
|
767
|
+
if (opts.minBaselineCoverage !== null) {
|
|
768
|
+
const ratio = results.length === 0 ? 1 : baseline_covered / results.length;
|
|
769
|
+
coverageGateFailed = ratio < opts.minBaselineCoverage;
|
|
770
|
+
}
|
|
771
|
+
|
|
436
772
|
if (opts.json) {
|
|
437
773
|
process.stdout.write(
|
|
438
|
-
JSON.stringify({ checked: results.length, failures: failures.length, drifted, drifted_files, no_baseline, no_baseline_files, results }, null, 2) + '\n',
|
|
774
|
+
JSON.stringify({ checked: results.length, failures: failures.length, drifted, drifted_files, no_baseline, no_baseline_files, baseline_covered, results }, null, 2) + '\n',
|
|
439
775
|
);
|
|
440
776
|
} else {
|
|
441
777
|
process.stdout.write(`# Hunk Verification Gate (#2969)\n\n`);
|
|
442
778
|
process.stdout.write(`Checked: ${results.length} file(s)\n`);
|
|
443
|
-
process.stdout.write(`Failures: ${failures.length}\n
|
|
779
|
+
process.stdout.write(`Failures: ${failures.length}\n`);
|
|
780
|
+
// #4135: the coverage headline is printed on EVERY run, before any
|
|
781
|
+
// per-file detail, so a near-zero-coverage green run can never render
|
|
782
|
+
// identically to a fully-verified one.
|
|
783
|
+
process.stdout.write(`${coverageHeadline(baseline_covered, results.length)}\n\n`);
|
|
784
|
+
if (baseline_covered < results.length) {
|
|
785
|
+
if (!opts.pristineDir) {
|
|
786
|
+
process.stdout.write(`No --pristine-dir: over-broad fallback — nothing was verified against a pristine baseline (#2998).\n\n`);
|
|
787
|
+
} else {
|
|
788
|
+
const skipped = results.filter((r) => r.reason !== null && !BASELINE_VERIFIED_REASONS.has(r.reason));
|
|
789
|
+
process.stdout.write(`## Files not verified against a pristine baseline\n\n`);
|
|
790
|
+
process.stdout.write(`Advisory (non-blocking): their user customizations may or may not have survived the merge.\n\n`);
|
|
791
|
+
for (const r of skipped) {
|
|
792
|
+
process.stdout.write(`- ${r.file} (${r.reason})\n`);
|
|
793
|
+
}
|
|
794
|
+
process.stdout.write('\n');
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (coverageGateFailed) {
|
|
798
|
+
process.stdout.write(`COVERAGE GATE FAILED: baseline coverage ${(opts.minBaselineCoverage * 100).toFixed(1)}% required, ${results.length === 0 ? 100 : ((baseline_covered / results.length) * 100).toFixed(1)}% achieved (#4135 strict mode).\n\n`);
|
|
799
|
+
}
|
|
444
800
|
if (failures.length > 0) {
|
|
445
801
|
process.stdout.write(`## Files with missing user-added content\n\n`);
|
|
446
802
|
for (const r of failures) {
|
|
@@ -456,11 +812,14 @@ function main() {
|
|
|
456
812
|
}
|
|
457
813
|
}
|
|
458
814
|
|
|
459
|
-
|
|
815
|
+
if (failures.length > 0) return 1;
|
|
816
|
+
if (coverageGateFailed) return 3;
|
|
817
|
+
return 0;
|
|
460
818
|
}
|
|
461
819
|
|
|
462
820
|
if (require.main === module) {
|
|
463
821
|
runMain(main);
|
|
464
822
|
}
|
|
465
823
|
|
|
466
|
-
module.exports = { computeUserAddedLines, isSignificantLine, verifyFile, walk, REASON, readPristineHashes, sha256, resolveInstalledPath, resolveSkillsRedirect };
|
|
824
|
+
module.exports = { computeUserAddedLines, isSignificantLine, verifyFile, classifyFile, walk, REASON, CLASSIFICATION, PRISTINE_RESOLUTION, resolvePristineBaseline, readPristineHashes, sha256, resolveInstalledPath, resolveSkillsRedirect, coverageHeadline, classifyBaselineCoverage };
|
|
825
|
+
|