shipwright-cli 3.1.0 → 3.3.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/agents/code-reviewer.md +2 -0
- package/.claude/agents/devops-engineer.md +2 -0
- package/.claude/agents/doc-fleet-agent.md +2 -0
- package/.claude/agents/pipeline-agent.md +2 -0
- package/.claude/agents/shell-script-specialist.md +2 -0
- package/.claude/agents/test-specialist.md +2 -0
- package/.claude/hooks/agent-crash-capture.sh +32 -0
- package/.claude/hooks/post-tool-use.sh +3 -2
- package/.claude/hooks/pre-tool-use.sh +35 -3
- package/README.md +22 -8
- package/claude-code/hooks/config-change.sh +18 -0
- package/claude-code/hooks/instructions-reloaded.sh +7 -0
- package/claude-code/hooks/worktree-create.sh +25 -0
- package/claude-code/hooks/worktree-remove.sh +20 -0
- package/config/code-constitution.json +130 -0
- package/config/defaults.json +25 -2
- package/config/policy.json +1 -1
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +8 -6
- package/dashboard/public/styles.css +176 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +117 -25
- package/dashboard/services/config.ts +26 -0
- package/dashboard/services/db.ts +118 -0
- package/dashboard/src/canvas/pixel-agent.ts +298 -0
- package/dashboard/src/canvas/pixel-sprites.ts +440 -0
- package/dashboard/src/canvas/shipyard-effects.ts +367 -0
- package/dashboard/src/canvas/shipyard-scene.ts +616 -0
- package/dashboard/src/canvas/submarine-layout.ts +267 -0
- package/dashboard/src/components/header.ts +8 -7
- package/dashboard/src/core/api.ts +5 -0
- package/dashboard/src/core/router.ts +1 -0
- package/dashboard/src/design/submarine-theme.ts +253 -0
- package/dashboard/src/main.ts +2 -0
- package/dashboard/src/types/api.ts +12 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/metrics.ts +69 -1
- package/dashboard/src/views/shipyard.ts +39 -0
- package/dashboard/types/index.ts +166 -0
- package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
- package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
- package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
- package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
- package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
- package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
- package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
- package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
- package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
- package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
- package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
- package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
- package/docs/research/RESEARCH_INDEX.md +439 -0
- package/docs/research/RESEARCH_SOURCES.md +440 -0
- package/docs/research/RESEARCH_SUMMARY.txt +275 -0
- package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
- package/package.json +2 -2
- package/scripts/lib/adaptive-model.sh +427 -0
- package/scripts/lib/adaptive-timeout.sh +316 -0
- package/scripts/lib/audit-trail.sh +309 -0
- package/scripts/lib/auto-recovery.sh +471 -0
- package/scripts/lib/bandit-selector.sh +431 -0
- package/scripts/lib/bootstrap.sh +104 -2
- package/scripts/lib/causal-graph.sh +455 -0
- package/scripts/lib/compat.sh +126 -0
- package/scripts/lib/compound-audit.sh +337 -0
- package/scripts/lib/constitutional.sh +454 -0
- package/scripts/lib/context-budget.sh +359 -0
- package/scripts/lib/convergence.sh +594 -0
- package/scripts/lib/cost-optimizer.sh +634 -0
- package/scripts/lib/daemon-adaptive.sh +14 -2
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +25 -4
- package/scripts/lib/daemon-poll-github.sh +361 -0
- package/scripts/lib/daemon-poll-health.sh +299 -0
- package/scripts/lib/daemon-poll.sh +27 -611
- package/scripts/lib/daemon-state.sh +119 -66
- package/scripts/lib/daemon-triage.sh +10 -0
- package/scripts/lib/dod-scorecard.sh +442 -0
- package/scripts/lib/error-actionability.sh +300 -0
- package/scripts/lib/formal-spec.sh +461 -0
- package/scripts/lib/helpers.sh +180 -5
- package/scripts/lib/intent-analysis.sh +409 -0
- package/scripts/lib/loop-convergence.sh +350 -0
- package/scripts/lib/loop-iteration.sh +682 -0
- package/scripts/lib/loop-progress.sh +48 -0
- package/scripts/lib/loop-restart.sh +185 -0
- package/scripts/lib/memory-effectiveness.sh +506 -0
- package/scripts/lib/mutation-executor.sh +352 -0
- package/scripts/lib/outcome-feedback.sh +521 -0
- package/scripts/lib/pipeline-cli.sh +336 -0
- package/scripts/lib/pipeline-commands.sh +1216 -0
- package/scripts/lib/pipeline-detection.sh +101 -3
- package/scripts/lib/pipeline-execution.sh +897 -0
- package/scripts/lib/pipeline-github.sh +28 -3
- package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
- package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
- package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
- package/scripts/lib/pipeline-intelligence.sh +104 -1138
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -711
- package/scripts/lib/pipeline-quality-gates.sh +563 -0
- package/scripts/lib/pipeline-stages-build.sh +730 -0
- package/scripts/lib/pipeline-stages-delivery.sh +965 -0
- package/scripts/lib/pipeline-stages-intake.sh +1133 -0
- package/scripts/lib/pipeline-stages-monitor.sh +407 -0
- package/scripts/lib/pipeline-stages-review.sh +1022 -0
- package/scripts/lib/pipeline-stages.sh +161 -2901
- package/scripts/lib/pipeline-state.sh +36 -5
- package/scripts/lib/pipeline-util.sh +487 -0
- package/scripts/lib/policy-learner.sh +438 -0
- package/scripts/lib/process-reward.sh +493 -0
- package/scripts/lib/project-detect.sh +649 -0
- package/scripts/lib/quality-profile.sh +334 -0
- package/scripts/lib/recruit-commands.sh +885 -0
- package/scripts/lib/recruit-learning.sh +739 -0
- package/scripts/lib/recruit-roles.sh +648 -0
- package/scripts/lib/reward-aggregator.sh +458 -0
- package/scripts/lib/rl-optimizer.sh +362 -0
- package/scripts/lib/root-cause.sh +427 -0
- package/scripts/lib/scope-enforcement.sh +445 -0
- package/scripts/lib/session-restart.sh +493 -0
- package/scripts/lib/skill-memory.sh +300 -0
- package/scripts/lib/skill-registry.sh +775 -0
- package/scripts/lib/spec-driven.sh +476 -0
- package/scripts/lib/test-helpers.sh +18 -7
- package/scripts/lib/test-holdout.sh +429 -0
- package/scripts/lib/test-optimizer.sh +511 -0
- package/scripts/shipwright-file-suggest.sh +45 -0
- package/scripts/skills/adversarial-quality.md +61 -0
- package/scripts/skills/api-design.md +44 -0
- package/scripts/skills/architecture-design.md +50 -0
- package/scripts/skills/brainstorming.md +43 -0
- package/scripts/skills/data-pipeline.md +44 -0
- package/scripts/skills/deploy-safety.md +64 -0
- package/scripts/skills/documentation.md +38 -0
- package/scripts/skills/frontend-design.md +45 -0
- package/scripts/skills/generated/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
- package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
- package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
- package/scripts/skills/generated/cli-version-management.md +29 -0
- package/scripts/skills/generated/collection-system-validation.md +99 -0
- package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
- package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
- package/scripts/skills/generated/test-parallelization-detection.md +65 -0
- package/scripts/skills/observability.md +79 -0
- package/scripts/skills/performance.md +48 -0
- package/scripts/skills/pr-quality.md +49 -0
- package/scripts/skills/product-thinking.md +43 -0
- package/scripts/skills/security-audit.md +49 -0
- package/scripts/skills/systematic-debugging.md +40 -0
- package/scripts/skills/testing-strategy.md +47 -0
- package/scripts/skills/two-stage-review.md +52 -0
- package/scripts/skills/validation-thoroughness.md +55 -0
- package/scripts/sw +9 -3
- package/scripts/sw-activity.sh +9 -8
- package/scripts/sw-adaptive.sh +8 -7
- package/scripts/sw-adversarial.sh +2 -1
- package/scripts/sw-architecture-enforcer.sh +3 -1
- package/scripts/sw-auth.sh +12 -2
- package/scripts/sw-autonomous.sh +5 -1
- package/scripts/sw-changelog.sh +4 -1
- package/scripts/sw-checkpoint.sh +2 -1
- package/scripts/sw-ci.sh +15 -6
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +45 -20
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +107 -5
- package/scripts/sw-daemon.sh +71 -11
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +71 -20
- package/scripts/sw-decide.sh +8 -2
- package/scripts/sw-decompose.sh +360 -17
- package/scripts/sw-deps.sh +4 -1
- package/scripts/sw-developer-simulation.sh +4 -1
- package/scripts/sw-discovery.sh +378 -5
- package/scripts/sw-doc-fleet.sh +4 -1
- package/scripts/sw-docs-agent.sh +3 -1
- package/scripts/sw-docs.sh +2 -1
- package/scripts/sw-doctor.sh +453 -2
- package/scripts/sw-dora.sh +4 -1
- package/scripts/sw-durable.sh +12 -7
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +13 -4
- package/scripts/sw-evidence.sh +364 -12
- package/scripts/sw-feedback.sh +550 -9
- package/scripts/sw-fix.sh +20 -1
- package/scripts/sw-fleet-discover.sh +6 -2
- package/scripts/sw-fleet-viz.sh +9 -4
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +18 -4
- package/scripts/sw-github-checks.sh +3 -2
- package/scripts/sw-github-deploy.sh +3 -2
- package/scripts/sw-github-graphql.sh +18 -7
- package/scripts/sw-guild.sh +5 -1
- package/scripts/sw-heartbeat.sh +5 -30
- package/scripts/sw-hello.sh +67 -0
- package/scripts/sw-hygiene.sh +10 -3
- package/scripts/sw-incident.sh +273 -5
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +44 -7
- package/scripts/sw-jira.sh +5 -1
- package/scripts/sw-launchd.sh +2 -1
- package/scripts/sw-linear.sh +4 -1
- package/scripts/sw-logs.sh +4 -1
- package/scripts/sw-loop.sh +436 -1076
- package/scripts/sw-memory.sh +357 -3
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +483 -27
- package/scripts/sw-otel.sh +15 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +7 -1
- package/scripts/sw-pipeline-vitals.sh +12 -6
- package/scripts/sw-pipeline.sh +54 -2653
- package/scripts/sw-pm.sh +16 -8
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +17 -5
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +17 -4
- package/scripts/sw-quality.sh +14 -6
- package/scripts/sw-reaper.sh +8 -25
- package/scripts/sw-recruit.sh +156 -2303
- package/scripts/sw-regression.sh +19 -12
- package/scripts/sw-release-manager.sh +3 -1
- package/scripts/sw-release.sh +4 -1
- package/scripts/sw-remote.sh +3 -1
- package/scripts/sw-replay.sh +7 -1
- package/scripts/sw-retro.sh +158 -1
- package/scripts/sw-review-rerun.sh +3 -1
- package/scripts/sw-scale.sh +14 -5
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +173 -6
- package/scripts/sw-session.sh +9 -3
- package/scripts/sw-setup.sh +3 -1
- package/scripts/sw-stall-detector.sh +406 -0
- package/scripts/sw-standup.sh +15 -7
- package/scripts/sw-status.sh +3 -1
- package/scripts/sw-strategic.sh +14 -6
- package/scripts/sw-stream.sh +13 -4
- package/scripts/sw-swarm.sh +20 -7
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +7 -31
- package/scripts/sw-testgen.sh +17 -6
- package/scripts/sw-tmux-pipeline.sh +4 -1
- package/scripts/sw-tmux-role-color.sh +2 -0
- package/scripts/sw-tmux-status.sh +1 -1
- package/scripts/sw-tmux.sh +37 -1
- package/scripts/sw-trace.sh +3 -1
- package/scripts/sw-tracker-github.sh +3 -0
- package/scripts/sw-tracker-jira.sh +3 -0
- package/scripts/sw-tracker-linear.sh +3 -0
- package/scripts/sw-tracker.sh +3 -1
- package/scripts/sw-triage.sh +3 -2
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +5 -2
- package/scripts/sw-widgets.sh +9 -4
- package/scripts/sw-worktree.sh +15 -3
- package/scripts/test-skill-injection.sh +1233 -0
- package/templates/pipelines/autonomous.json +27 -3
- package/templates/pipelines/cost-aware.json +34 -8
- package/templates/pipelines/deployed.json +12 -0
- package/templates/pipelines/enterprise.json +12 -0
- package/templates/pipelines/fast.json +6 -0
- package/templates/pipelines/full.json +27 -3
- package/templates/pipelines/hotfix.json +6 -0
- package/templates/pipelines/standard.json +12 -0
- package/templates/pipelines/tdd.json +12 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
|
-
VERSION="3.
|
|
8
|
+
VERSION="3.3.0"
|
|
9
9
|
|
|
10
10
|
# ─── Script directory resolution ────────────────────────────────────────────
|
|
11
11
|
SOURCE="${BASH_SOURCE[0]}"
|
|
@@ -19,6 +19,7 @@ SCRIPT_DIR="$(cd "$(dirname "$SOURCE")" && pwd)"
|
|
|
19
19
|
# ─── State directories ──────────────────────────────────────────────────────
|
|
20
20
|
E2E_DIR="${HOME}/.shipwright/e2e"
|
|
21
21
|
SUITE_REGISTRY="$E2E_DIR/suite-registry.json"
|
|
22
|
+
# shellcheck disable=SC2034
|
|
22
23
|
FLAKY_CACHE="$E2E_DIR/flaky-cache.json"
|
|
23
24
|
RESULTS_LOG="$E2E_DIR/results.jsonl"
|
|
24
25
|
LATEST_REPORT="$E2E_DIR/latest-report.json"
|
|
@@ -123,7 +124,7 @@ cmd_register() {
|
|
|
123
124
|
fi
|
|
124
125
|
|
|
125
126
|
# Parse existing registry
|
|
126
|
-
local registry=$(load_registry)
|
|
127
|
+
local registry; registry=$(load_registry)
|
|
127
128
|
local new_suite
|
|
128
129
|
|
|
129
130
|
# Create feature array
|
|
@@ -179,7 +180,7 @@ cmd_quarantine() {
|
|
|
179
180
|
init_registry
|
|
180
181
|
fi
|
|
181
182
|
|
|
182
|
-
local registry=$(load_registry)
|
|
183
|
+
local registry; registry=$(load_registry)
|
|
183
184
|
|
|
184
185
|
if [[ "$action" == "quarantine" ]]; then
|
|
185
186
|
# Add to quarantine list if not already present
|
|
@@ -207,20 +208,20 @@ cmd_quarantine() {
|
|
|
207
208
|
# ─── Run a single test suite ────────────────────────────────────────────────
|
|
208
209
|
run_suite() {
|
|
209
210
|
local suite_id="$1"
|
|
210
|
-
local registry=$(load_registry)
|
|
211
|
+
local registry; registry=$(load_registry)
|
|
211
212
|
|
|
212
213
|
# Find suite
|
|
213
|
-
local suite=$(echo "$registry" | jq ".suites[] | select(.id == \"$suite_id\")")
|
|
214
|
+
local suite; suite=$(echo "$registry" | jq ".suites[] | select(.id == \"$suite_id\")")
|
|
214
215
|
|
|
215
216
|
if [[ -z "$suite" ]]; then
|
|
216
217
|
error "Suite not found: $suite_id"
|
|
217
218
|
return 1
|
|
218
219
|
fi
|
|
219
220
|
|
|
220
|
-
local suite_name=$(echo "$suite" | jq -r '.name')
|
|
221
|
-
local script=$(echo "$suite" | jq -r '.script')
|
|
222
|
-
local timeout=$(echo "$suite" | jq -r '.timeout_seconds')
|
|
223
|
-
local features=$(echo "$suite" | jq -r '.features | join(", ")')
|
|
221
|
+
local suite_name; suite_name=$(echo "$suite" | jq -r '.name')
|
|
222
|
+
local script; script=$(echo "$suite" | jq -r '.script')
|
|
223
|
+
local timeout; timeout=$(echo "$suite" | jq -r '.timeout_seconds')
|
|
224
|
+
local features; features=$(echo "$suite" | jq -r '.features | join(", ")')
|
|
224
225
|
|
|
225
226
|
local test_script="$SCRIPT_DIR/$script"
|
|
226
227
|
|
|
@@ -230,7 +231,7 @@ run_suite() {
|
|
|
230
231
|
fi
|
|
231
232
|
|
|
232
233
|
info "Running: $suite_name ($features)"
|
|
233
|
-
local start_time=$(date +%s)
|
|
234
|
+
local start_time; start_time=$(date +%s)
|
|
234
235
|
|
|
235
236
|
# Run with timeout (gtimeout on macOS, timeout on Linux)
|
|
236
237
|
local timeout_cmd="timeout"
|
|
@@ -238,7 +239,7 @@ run_suite() {
|
|
|
238
239
|
local exit_code=0
|
|
239
240
|
"$timeout_cmd" "$timeout" bash "$test_script" || exit_code=$?
|
|
240
241
|
|
|
241
|
-
local end_time=$(date +%s)
|
|
242
|
+
local end_time; end_time=$(date +%s)
|
|
242
243
|
local duration=$((end_time - start_time))
|
|
243
244
|
|
|
244
245
|
# Log result
|
|
@@ -257,9 +258,9 @@ run_parallel() {
|
|
|
257
258
|
local max_parallel=${2:-3}
|
|
258
259
|
|
|
259
260
|
ensure_state_dir
|
|
260
|
-
> "$RESULTS_LOG" # Clear results log
|
|
261
|
+
true > "$RESULTS_LOG" # Clear results log
|
|
261
262
|
|
|
262
|
-
local registry=$(load_registry)
|
|
263
|
+
local registry; registry=$(load_registry)
|
|
263
264
|
|
|
264
265
|
# Filter suites by category and enabled status
|
|
265
266
|
local suites
|
|
@@ -341,7 +342,7 @@ cmd_report() {
|
|
|
341
342
|
local skip=0
|
|
342
343
|
|
|
343
344
|
while IFS= read -r line; do
|
|
344
|
-
local exit_code=$(echo "$line" | jq -r '.exit_code // 0')
|
|
345
|
+
local exit_code; exit_code=$(echo "$line" | jq -r '.exit_code // 0')
|
|
345
346
|
if [[ $exit_code -eq 0 ]]; then
|
|
346
347
|
pass=$((pass + 1))
|
|
347
348
|
elif [[ $exit_code -eq 124 ]]; then
|
|
@@ -354,7 +355,7 @@ cmd_report() {
|
|
|
354
355
|
local total=$((pass + fail + timeout + skip))
|
|
355
356
|
|
|
356
357
|
# Create report
|
|
357
|
-
local report=$(jq -n \
|
|
358
|
+
local report; report=$(jq -n \
|
|
358
359
|
--arg ts "$(date -Iseconds)" \
|
|
359
360
|
--arg version "$VERSION" \
|
|
360
361
|
--argjson p "$pass" \
|
|
@@ -409,7 +410,7 @@ cmd_flaky() {
|
|
|
409
410
|
info "Analyzing flaky tests..."
|
|
410
411
|
|
|
411
412
|
# Group by test name, count passes/fails
|
|
412
|
-
local flaky_analysis=$(jq -s 'group_by(.suite_id) | map({
|
|
413
|
+
local flaky_analysis; flaky_analysis=$(jq -s 'group_by(.suite_id) | map({
|
|
413
414
|
test: .[0].suite_id,
|
|
414
415
|
runs: length,
|
|
415
416
|
passes: (map(select(.exit_code == 0)) | length),
|
package/scripts/sw-eventbus.sh
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
# shellcheck disable=SC2034
|
|
11
|
+
VERSION="3.3.0"
|
|
11
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
13
|
|
|
13
14
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -31,6 +32,7 @@ fi
|
|
|
31
32
|
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
32
33
|
emit_event() {
|
|
33
34
|
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
35
|
+
# shellcheck disable=SC2155
|
|
34
36
|
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
35
37
|
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
36
38
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
@@ -94,6 +96,8 @@ cmd_subscribe() {
|
|
|
94
96
|
while true; do
|
|
95
97
|
if db_available 2>/dev/null; then
|
|
96
98
|
local events batch_last_id=0
|
|
99
|
+
# Numeric validation for last_id
|
|
100
|
+
[[ ! "$last_id" =~ ^[0-9]+$ ]] && last_id=0
|
|
97
101
|
events=$(sqlite3 -json "$DB_FILE" "SELECT * FROM events WHERE id > $last_id ORDER BY id ASC LIMIT 50;" 2>/dev/null || echo "[]")
|
|
98
102
|
if [[ "$events" != "[]" && -n "$events" ]]; then
|
|
99
103
|
while IFS= read -r event; do
|
|
@@ -124,6 +128,7 @@ cmd_subscribe() {
|
|
|
124
128
|
|
|
125
129
|
# ─── Process reaper (SIGCHLD monitor) ──────────────────────────────────────
|
|
126
130
|
cmd_reaper() {
|
|
131
|
+
# shellcheck disable=SC2034
|
|
127
132
|
local pid_list=()
|
|
128
133
|
|
|
129
134
|
info "Starting process reaper. Press Ctrl+C to exit."
|
|
@@ -145,6 +150,7 @@ cmd_reaper() {
|
|
|
145
150
|
# Check if process is still alive
|
|
146
151
|
if ! kill -0 "$pid" 2>/dev/null; then
|
|
147
152
|
# Process died — emit event
|
|
153
|
+
# shellcheck disable=SC2155
|
|
148
154
|
local payload="{\"pid\": $pid, \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
|
|
149
155
|
cmd_publish "process.exited" "reaper" "$(generate_uuid)" "$payload"
|
|
150
156
|
fi
|
|
@@ -251,7 +257,8 @@ cmd_status() {
|
|
|
251
257
|
fi
|
|
252
258
|
elif [[ -f "$EVENTS_FILE" ]]; then
|
|
253
259
|
local total_events last_event_ts
|
|
254
|
-
total_events=$(wc -l < "$EVENTS_FILE" ||
|
|
260
|
+
total_events=$(wc -l < "$EVENTS_FILE" || true)
|
|
261
|
+
total_events="${total_events:-0}"
|
|
255
262
|
last_event_ts=$(tail -1 "$EVENTS_FILE" | jq -r '.ts // "never"' 2>/dev/null || echo "never")
|
|
256
263
|
echo -e " ${CYAN}Event Store:${RESET} $EVENTS_FILE (file fallback)"
|
|
257
264
|
echo -e " ${CYAN}Total Events:${RESET} ${BOLD}${total_events}${RESET}"
|
|
@@ -290,7 +297,8 @@ cmd_clean() {
|
|
|
290
297
|
elif [[ -f "$EVENTS_FILE" ]]; then
|
|
291
298
|
info "Cleaning events older than ${ttl_days} days..."
|
|
292
299
|
local old_count tmp_file new_count removed
|
|
293
|
-
old_count=$(grep -c "ts" "$EVENTS_FILE" 2>/dev/null ||
|
|
300
|
+
old_count=$(grep -c "ts" "$EVENTS_FILE" 2>/dev/null || true)
|
|
301
|
+
old_count="${old_count:-0}"
|
|
294
302
|
tmp_file="$(mktemp)"
|
|
295
303
|
while IFS= read -r line; do
|
|
296
304
|
[[ -z "$line" ]] && continue
|
|
@@ -299,7 +307,8 @@ cmd_clean() {
|
|
|
299
307
|
[[ -n "$ts" && "$ts" > "$cutoff_iso" ]] && echo "$line" >> "$tmp_file"
|
|
300
308
|
done < "$EVENTS_FILE"
|
|
301
309
|
mv "$tmp_file" "$EVENTS_FILE"
|
|
302
|
-
new_count=$(wc -l < "$EVENTS_FILE" ||
|
|
310
|
+
new_count=$(wc -l < "$EVENTS_FILE" || true)
|
|
311
|
+
new_count="${new_count:-0}"
|
|
303
312
|
removed=$((old_count - new_count))
|
|
304
313
|
success "Removed $removed old events. Remaining: $new_count"
|
|
305
314
|
else
|
package/scripts/sw-evidence.sh
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
# shellcheck disable=SC2034
|
|
12
|
+
VERSION="3.3.0"
|
|
12
13
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
14
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
14
15
|
|
|
@@ -187,6 +188,7 @@ collect_api() {
|
|
|
187
188
|
local name="$1"
|
|
188
189
|
local collector_json="$2"
|
|
189
190
|
|
|
191
|
+
# shellcheck disable=SC2034
|
|
190
192
|
local url method expected_status headers_json body timeout
|
|
191
193
|
url=$(echo "$collector_json" | jq -r '.url // ""')
|
|
192
194
|
method=$(echo "$collector_json" | jq -r '.method // "GET"')
|
|
@@ -444,6 +446,191 @@ collect_custom() {
|
|
|
444
446
|
'{command: $cmd, exit_code: $exit_code, expected_exit_code: $expected, output_preview: $output_preview}')"
|
|
445
447
|
}
|
|
446
448
|
|
|
449
|
+
# ─── Mutation Testing: Verify mutations are caught by test suite ───────────────
|
|
450
|
+
|
|
451
|
+
collect_mutation() {
|
|
452
|
+
local name="$1"
|
|
453
|
+
local collector_json="$2"
|
|
454
|
+
|
|
455
|
+
local test_cmd target_files threshold
|
|
456
|
+
test_cmd=$(echo "$collector_json" | jq -r '.testCommand // ""')
|
|
457
|
+
target_files=$(echo "$collector_json" | jq -r '.targetFiles // ""')
|
|
458
|
+
threshold=$(echo "$collector_json" | jq -r '.mutationThreshold // 60')
|
|
459
|
+
|
|
460
|
+
if [[ -z "$test_cmd" ]] || [[ -z "$target_files" ]]; then
|
|
461
|
+
error "[mutation] ${name}: testCommand and targetFiles required"
|
|
462
|
+
write_evidence_record "$name" "mutation" "false" '{"error": "testCommand and targetFiles required"}'
|
|
463
|
+
return
|
|
464
|
+
fi
|
|
465
|
+
|
|
466
|
+
info "[mutation] ${name}: testing mutation coverage (threshold: ${threshold}%)"
|
|
467
|
+
|
|
468
|
+
local mutation_dir
|
|
469
|
+
mutation_dir=$(mktemp -d "${TMPDIR:-/tmp}/sw-evidence-mutations.XXXXXX")
|
|
470
|
+
trap "rm -rf '$mutation_dir'" RETURN
|
|
471
|
+
|
|
472
|
+
local total_mutants=0
|
|
473
|
+
local killed_mutants=0
|
|
474
|
+
|
|
475
|
+
# For each target file, create mutations
|
|
476
|
+
local file
|
|
477
|
+
while IFS= read -r file; do
|
|
478
|
+
[[ -z "$file" ]] && continue
|
|
479
|
+
[[ ! -f "$REPO_DIR/$file" ]] && continue
|
|
480
|
+
|
|
481
|
+
# Copy file to mutation dir for testing
|
|
482
|
+
local file_copy="$mutation_dir/$(basename "$file")"
|
|
483
|
+
cp "$REPO_DIR/$file" "$file_copy"
|
|
484
|
+
|
|
485
|
+
# Apply mutations: swap operators, negate conditions, change exit codes
|
|
486
|
+
local mutations=()
|
|
487
|
+
|
|
488
|
+
# Mutation 1: swap == to !=
|
|
489
|
+
if grep -q "==" "$file_copy"; then
|
|
490
|
+
mutations+=("sed 's/==/!=/g'")
|
|
491
|
+
fi
|
|
492
|
+
|
|
493
|
+
# Mutation 2: swap != to ==
|
|
494
|
+
if grep -q "!=" "$file_copy"; then
|
|
495
|
+
mutations+=("sed 's/!=/==/g'")
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
# Mutation 3: change -gt to -lt
|
|
499
|
+
if grep -q "\-gt" "$file_copy"; then
|
|
500
|
+
mutations+=("sed 's/-gt/-lt/g'")
|
|
501
|
+
fi
|
|
502
|
+
|
|
503
|
+
# Mutation 4: change -lt to -gt
|
|
504
|
+
if grep -q "\-lt" "$file_copy"; then
|
|
505
|
+
mutations+=("sed 's/-lt/-gt/g'")
|
|
506
|
+
fi
|
|
507
|
+
|
|
508
|
+
# Mutation 5: change exit 0 to exit 1
|
|
509
|
+
if grep -q "exit 0" "$file_copy"; then
|
|
510
|
+
mutations+=("sed 's/exit 0/exit 1/g'")
|
|
511
|
+
fi
|
|
512
|
+
|
|
513
|
+
# Mutation 6: comment out error traps
|
|
514
|
+
if grep -q "trap.*ERR" "$file_copy"; then
|
|
515
|
+
mutations+=("sed 's/^trap /#trap /g'")
|
|
516
|
+
fi
|
|
517
|
+
|
|
518
|
+
# Apply each mutation and test
|
|
519
|
+
for i in "${!mutations[@]}"; do
|
|
520
|
+
total_mutants=$((total_mutants + 1))
|
|
521
|
+
local mutated_copy="$file_copy.mutant.$i"
|
|
522
|
+
cp "$file_copy" "$mutated_copy"
|
|
523
|
+
|
|
524
|
+
# Apply mutation
|
|
525
|
+
eval "${mutations[$i]} \"$mutated_copy\" > \"${mutated_copy}.tmp\" && mv \"${mutated_copy}.tmp\" \"$mutated_copy\"" 2>/dev/null || true
|
|
526
|
+
|
|
527
|
+
# Run test with mutation — test should fail (mutation caught)
|
|
528
|
+
local test_result=0
|
|
529
|
+
(cd "$REPO_DIR" && _run_with_timeout 30 bash -c "$test_cmd" > /dev/null 2>&1) || test_result=$?
|
|
530
|
+
|
|
531
|
+
# If test failed (non-zero), mutation was caught
|
|
532
|
+
if [[ "$test_result" -ne 0 ]]; then
|
|
533
|
+
killed_mutants=$((killed_mutants + 1))
|
|
534
|
+
fi
|
|
535
|
+
|
|
536
|
+
rm -f "$mutated_copy"
|
|
537
|
+
done
|
|
538
|
+
|
|
539
|
+
done <<< "$target_files"
|
|
540
|
+
|
|
541
|
+
local mutation_score=0
|
|
542
|
+
local passed="false"
|
|
543
|
+
if [[ "$total_mutants" -gt 0 ]]; then
|
|
544
|
+
mutation_score=$((killed_mutants * 100 / total_mutants))
|
|
545
|
+
[[ "$mutation_score" -ge "$threshold" ]] && passed="true"
|
|
546
|
+
fi
|
|
547
|
+
|
|
548
|
+
write_evidence_record "$name" "mutation" "$passed" \
|
|
549
|
+
"$(jq -n --argjson total "$total_mutants" --argjson killed "$killed_mutants" \
|
|
550
|
+
--argjson score "$mutation_score" --argjson threshold "$threshold" \
|
|
551
|
+
'{total_mutants: $total, killed_mutants: $killed, mutation_score: $score, threshold: $threshold}')"
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
# ─── Property-Based Testing: Verify properties hold over iterations ──────────
|
|
555
|
+
|
|
556
|
+
collect_property() {
|
|
557
|
+
local name="$1"
|
|
558
|
+
local collector_json="$2"
|
|
559
|
+
|
|
560
|
+
local property_cmd iterations
|
|
561
|
+
property_cmd=$(echo "$collector_json" | jq -r '.propertyCommand // ""')
|
|
562
|
+
iterations=$(echo "$collector_json" | jq -r '.iterations // 100')
|
|
563
|
+
|
|
564
|
+
if [[ -z "$property_cmd" ]]; then
|
|
565
|
+
error "[property] ${name}: propertyCommand required"
|
|
566
|
+
write_evidence_record "$name" "property" "false" '{"error": "propertyCommand required"}'
|
|
567
|
+
return
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
info "[property] ${name}: running property test (${iterations} iterations)"
|
|
571
|
+
|
|
572
|
+
local passed_count=0
|
|
573
|
+
local failed_count=0
|
|
574
|
+
local counterexamples="[]"
|
|
575
|
+
|
|
576
|
+
# Run property test multiple times
|
|
577
|
+
local i
|
|
578
|
+
for ((i = 0; i < iterations; i++)); do
|
|
579
|
+
local output=0
|
|
580
|
+
local result_output=""
|
|
581
|
+
result_output=$(cd "$REPO_DIR" && _run_with_timeout 10 bash -c "$property_cmd" 2>&1) || output=$?
|
|
582
|
+
|
|
583
|
+
if [[ "$output" -eq 0 ]]; then
|
|
584
|
+
passed_count=$((passed_count + 1))
|
|
585
|
+
else
|
|
586
|
+
failed_count=$((failed_count + 1))
|
|
587
|
+
# Capture counterexample (first 200 chars of output)
|
|
588
|
+
counterexamples=$(echo "$counterexamples" | jq \
|
|
589
|
+
--arg ce "${result_output:0:200}" \
|
|
590
|
+
'. += [{"iteration": '$i', "output": $ce}]')
|
|
591
|
+
fi
|
|
592
|
+
done
|
|
593
|
+
|
|
594
|
+
local passed="false"
|
|
595
|
+
[[ "$failed_count" -eq 0 ]] && passed="true"
|
|
596
|
+
|
|
597
|
+
write_evidence_record "$name" "property" "$passed" \
|
|
598
|
+
"$(jq -n --argjson passed_count "$passed_count" --argjson failed_count "$failed_count" \
|
|
599
|
+
--argjson iterations "$iterations" --argjson counterexamples "$counterexamples" \
|
|
600
|
+
'{passed_count: $passed_count, failed_count: $failed_count, total_iterations: $iterations, counterexamples: $counterexamples}')"
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
# ─── Invariant Checking: Verify system invariants ────────────────────────────
|
|
604
|
+
|
|
605
|
+
collect_invariant() {
|
|
606
|
+
local name="$1"
|
|
607
|
+
local collector_json="$2"
|
|
608
|
+
|
|
609
|
+
local check_cmd invariant_name
|
|
610
|
+
check_cmd=$(echo "$collector_json" | jq -r '.checkCommand // ""')
|
|
611
|
+
invariant_name=$(echo "$collector_json" | jq -r '.invariantName // "unnamed"')
|
|
612
|
+
|
|
613
|
+
if [[ -z "$check_cmd" ]]; then
|
|
614
|
+
error "[invariant] ${name}: checkCommand required"
|
|
615
|
+
write_evidence_record "$name" "invariant" "false" '{"error": "checkCommand required"}'
|
|
616
|
+
return
|
|
617
|
+
fi
|
|
618
|
+
|
|
619
|
+
info "[invariant] ${name}: checking invariant '${invariant_name}'"
|
|
620
|
+
|
|
621
|
+
local exit_code=0
|
|
622
|
+
local output=""
|
|
623
|
+
output=$(cd "$REPO_DIR" && _run_with_timeout 30 bash -c "$check_cmd" 2>&1) || exit_code=$?
|
|
624
|
+
|
|
625
|
+
local passed="false"
|
|
626
|
+
[[ "$exit_code" -eq 0 ]] && passed="true"
|
|
627
|
+
|
|
628
|
+
write_evidence_record "$name" "invariant" "$passed" \
|
|
629
|
+
"$(jq -n --arg invariant_name "$invariant_name" --argjson exit_code "$exit_code" \
|
|
630
|
+
--arg output "${output:0:2000}" \
|
|
631
|
+
'{invariant_name: $invariant_name, check_exit_code: $exit_code, output: $output}')"
|
|
632
|
+
}
|
|
633
|
+
|
|
447
634
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
448
635
|
# EVIDENCE RECORD WRITER
|
|
449
636
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
@@ -477,6 +664,142 @@ write_evidence_record() {
|
|
|
477
664
|
fi
|
|
478
665
|
}
|
|
479
666
|
|
|
667
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
668
|
+
# ARTIFACT CAPTURE
|
|
669
|
+
# Stores build logs, test reports, coverage as evidence artifacts with manifest
|
|
670
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
671
|
+
|
|
672
|
+
evidence_capture_artifact() {
|
|
673
|
+
local artifact_name="$1"
|
|
674
|
+
local artifact_path="$2"
|
|
675
|
+
|
|
676
|
+
if [[ ! -f "$artifact_path" ]]; then
|
|
677
|
+
warn "[artifact] ${artifact_name}: file not found"
|
|
678
|
+
return 1
|
|
679
|
+
fi
|
|
680
|
+
|
|
681
|
+
local artifacts_dir="${EVIDENCE_DIR}/artifacts"
|
|
682
|
+
mkdir -p "$artifacts_dir"
|
|
683
|
+
|
|
684
|
+
# Copy artifact to artifacts directory
|
|
685
|
+
local dest_file="$artifacts_dir/${artifact_name}"
|
|
686
|
+
cp "$artifact_path" "$dest_file"
|
|
687
|
+
|
|
688
|
+
# Compute SHA-256 of artifact
|
|
689
|
+
local artifact_sha256
|
|
690
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
691
|
+
artifact_sha256=$(shasum -a 256 "$dest_file" | awk '{print $1}')
|
|
692
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
693
|
+
artifact_sha256=$(sha256sum "$dest_file" | awk '{print $1}')
|
|
694
|
+
else
|
|
695
|
+
artifact_sha256="unknown"
|
|
696
|
+
fi
|
|
697
|
+
|
|
698
|
+
info "[artifact] ${artifact_name}: captured (${artifact_sha256:0:16}...)"
|
|
699
|
+
|
|
700
|
+
# Append to artifacts manifest
|
|
701
|
+
local artifacts_manifest="${EVIDENCE_DIR}/artifacts-manifest.json"
|
|
702
|
+
if [[ ! -f "$artifacts_manifest" ]]; then
|
|
703
|
+
echo "[]" > "$artifacts_manifest"
|
|
704
|
+
fi
|
|
705
|
+
|
|
706
|
+
local tmp_manifest
|
|
707
|
+
tmp_manifest=$(mktemp "${TMPDIR:-/tmp}/sw-evidence-artifacts.XXXXXX")
|
|
708
|
+
jq \
|
|
709
|
+
--arg name "$artifact_name" \
|
|
710
|
+
--arg path "$dest_file" \
|
|
711
|
+
--arg sha256 "$artifact_sha256" \
|
|
712
|
+
--arg captured_at "$(now_iso)" \
|
|
713
|
+
'. += [{"name": $name, "path": $path, "sha256": $sha256, "captured_at": $captured_at}]' \
|
|
714
|
+
"$artifacts_manifest" > "$tmp_manifest"
|
|
715
|
+
mv "$tmp_manifest" "$artifacts_manifest"
|
|
716
|
+
|
|
717
|
+
return 0
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
721
|
+
# QUALITY SCORE COMPUTATION
|
|
722
|
+
# Weights: mutation (30%), property tests (25%), invariants (25%), collectors (20%)
|
|
723
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
724
|
+
|
|
725
|
+
evidence_quality_score() {
|
|
726
|
+
ensure_evidence_dir
|
|
727
|
+
|
|
728
|
+
if [[ ! -f "$MANIFEST_FILE" ]]; then
|
|
729
|
+
echo "0"
|
|
730
|
+
return 1
|
|
731
|
+
fi
|
|
732
|
+
|
|
733
|
+
# Extract collector results
|
|
734
|
+
local mutation_score=0
|
|
735
|
+
local property_score=0
|
|
736
|
+
local invariant_score=0
|
|
737
|
+
local collector_pass_rate=0
|
|
738
|
+
|
|
739
|
+
# Mutation test score (from manifest)
|
|
740
|
+
local mutation_evidence="${EVIDENCE_DIR}/mutation_test.json"
|
|
741
|
+
if [[ -f "$mutation_evidence" ]]; then
|
|
742
|
+
local mutation_passed
|
|
743
|
+
mutation_passed=$(jq -r '.passed // false' "$mutation_evidence" 2>/dev/null || echo "false")
|
|
744
|
+
if [[ "$mutation_passed" == "true" ]]; then
|
|
745
|
+
mutation_score=$(jq -r '.details.mutation_score // 0' "$mutation_evidence" 2>/dev/null || echo "0")
|
|
746
|
+
[[ -z "$mutation_score" ]] && mutation_score="0"
|
|
747
|
+
fi
|
|
748
|
+
fi
|
|
749
|
+
|
|
750
|
+
# Property test score (failed_count == 0 => 100, else 0)
|
|
751
|
+
local property_evidence="${EVIDENCE_DIR}/property_test.json"
|
|
752
|
+
if [[ -f "$property_evidence" ]]; then
|
|
753
|
+
local prop_failed
|
|
754
|
+
prop_failed=$(jq -r '.details.failed_count // 0' "$property_evidence" 2>/dev/null || echo "0")
|
|
755
|
+
[[ -z "$prop_failed" ]] && prop_failed="0"
|
|
756
|
+
if [[ "$prop_failed" -eq 0 ]]; then
|
|
757
|
+
property_score=100
|
|
758
|
+
fi
|
|
759
|
+
fi
|
|
760
|
+
|
|
761
|
+
# Invariant score (all pass => 100, else 50)
|
|
762
|
+
local invariant_evidence="${EVIDENCE_DIR}/invariant_check.json"
|
|
763
|
+
if [[ -f "$invariant_evidence" ]]; then
|
|
764
|
+
local invariant_passed
|
|
765
|
+
invariant_passed=$(jq -r '.passed // false' "$invariant_evidence" 2>/dev/null || echo "false")
|
|
766
|
+
if [[ "$invariant_passed" == "true" ]]; then
|
|
767
|
+
invariant_score=100
|
|
768
|
+
else
|
|
769
|
+
invariant_score=50
|
|
770
|
+
fi
|
|
771
|
+
fi
|
|
772
|
+
|
|
773
|
+
# Collector pass rate
|
|
774
|
+
local collector_count
|
|
775
|
+
collector_count=$(jq -r '.collector_count // 0' "$MANIFEST_FILE" 2>/dev/null || echo "0")
|
|
776
|
+
[[ -z "$collector_count" ]] && collector_count="0"
|
|
777
|
+
|
|
778
|
+
local passed_count
|
|
779
|
+
passed_count=$(jq -r '.passed // 0' "$MANIFEST_FILE" 2>/dev/null || echo "0")
|
|
780
|
+
[[ -z "$passed_count" ]] && passed_count="0"
|
|
781
|
+
|
|
782
|
+
if [[ "$collector_count" -gt 0 ]]; then
|
|
783
|
+
collector_pass_rate=$((passed_count * 100 / collector_count))
|
|
784
|
+
fi
|
|
785
|
+
|
|
786
|
+
# Weighted score: 30% mutation + 25% property + 25% invariant + 20% collector
|
|
787
|
+
local weighted_score=0
|
|
788
|
+
weighted_score=$((
|
|
789
|
+
(mutation_score * 30) +
|
|
790
|
+
(property_score * 25) +
|
|
791
|
+
(invariant_score * 25) +
|
|
792
|
+
(collector_pass_rate * 20)
|
|
793
|
+
))
|
|
794
|
+
weighted_score=$((weighted_score / 100))
|
|
795
|
+
|
|
796
|
+
# Cap at 100
|
|
797
|
+
[[ "$weighted_score" -gt 100 ]] && weighted_score=100
|
|
798
|
+
|
|
799
|
+
echo "$weighted_score"
|
|
800
|
+
return 0
|
|
801
|
+
}
|
|
802
|
+
|
|
480
803
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
481
804
|
# COMMANDS
|
|
482
805
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
@@ -514,6 +837,9 @@ cmd_capture() {
|
|
|
514
837
|
database) collect_database "$cname" "$collector" ;;
|
|
515
838
|
webhook) collect_webhook "$cname" "$collector" ;;
|
|
516
839
|
custom) collect_custom "$cname" "$collector" ;;
|
|
840
|
+
mutation) collect_mutation "$cname" "$collector" ;;
|
|
841
|
+
property) collect_property "$cname" "$collector" ;;
|
|
842
|
+
invariant) collect_invariant "$cname" "$collector" ;;
|
|
517
843
|
*) warn "Unknown collector type: ${ctype} (skipping ${cname})" ; continue ;;
|
|
518
844
|
esac
|
|
519
845
|
|
|
@@ -675,26 +1001,40 @@ cmd_status() {
|
|
|
675
1001
|
cmd_list_types() {
|
|
676
1002
|
echo "Supported evidence types:"
|
|
677
1003
|
echo ""
|
|
678
|
-
echo " browser
|
|
679
|
-
echo " api
|
|
680
|
-
echo " database
|
|
681
|
-
echo " cli
|
|
682
|
-
echo " webhook
|
|
683
|
-
echo " custom
|
|
1004
|
+
echo " browser HTTP page load — verifies UI renders correctly"
|
|
1005
|
+
echo " api REST/GraphQL endpoint — verifies response status, body, content-type"
|
|
1006
|
+
echo " database Schema/migration check — verifies DB integrity via command"
|
|
1007
|
+
echo " cli Command execution — verifies exit code and output"
|
|
1008
|
+
echo " webhook Callback verification — verifies webhook endpoint responds"
|
|
1009
|
+
echo " custom User-defined script — any verification logic"
|
|
1010
|
+
echo " mutation Mutation testing — verifies test suite catches code mutations"
|
|
1011
|
+
echo " property Property-based testing — verifies properties hold over iterations"
|
|
1012
|
+
echo " invariant Invariant checking — verifies system invariants hold"
|
|
684
1013
|
echo ""
|
|
685
1014
|
echo "Configure collectors in config/policy.json under the 'evidence' section."
|
|
686
1015
|
}
|
|
687
1016
|
|
|
1017
|
+
cmd_quality_score() {
|
|
1018
|
+
ensure_evidence_dir
|
|
1019
|
+
local score
|
|
1020
|
+
score=$(evidence_quality_score)
|
|
1021
|
+
echo "Evidence-based quality score: ${score}/100"
|
|
1022
|
+
emit_event "evidence.quality_score" "score=${score}"
|
|
1023
|
+
return 0
|
|
1024
|
+
}
|
|
1025
|
+
|
|
688
1026
|
show_help() {
|
|
689
1027
|
cat << 'EOF'
|
|
690
1028
|
Usage: shipwright evidence <command> [args]
|
|
691
1029
|
|
|
692
1030
|
Commands:
|
|
693
|
-
capture [type]
|
|
694
|
-
verify
|
|
695
|
-
pre-pr [type]
|
|
696
|
-
status
|
|
697
|
-
types
|
|
1031
|
+
capture [type] Capture evidence (optionally filter by type)
|
|
1032
|
+
verify Verify evidence manifest and freshness
|
|
1033
|
+
pre-pr [type] Capture + verify (run before PR creation)
|
|
1034
|
+
status Show current evidence state grouped by type
|
|
1035
|
+
types List supported evidence types
|
|
1036
|
+
quality-score Compute quality score from evidence
|
|
1037
|
+
artifact <name> <path> Capture artifact with SHA-256 manifest
|
|
698
1038
|
|
|
699
1039
|
Evidence Types:
|
|
700
1040
|
browser HTTP page load verification
|
|
@@ -703,11 +1043,17 @@ Evidence Types:
|
|
|
703
1043
|
cli Command execution and exit code
|
|
704
1044
|
webhook Callback endpoint verification
|
|
705
1045
|
custom User-defined verification scripts
|
|
1046
|
+
mutation Mutation testing (verify test suite catches mutations)
|
|
1047
|
+
property Property-based testing (verify properties hold)
|
|
1048
|
+
invariant Invariant checking (verify system invariants)
|
|
706
1049
|
|
|
707
1050
|
Evidence collectors are defined in config/policy.json under the
|
|
708
1051
|
'evidence.collectors' array. Each collector specifies a type,
|
|
709
1052
|
target, and assertions.
|
|
710
1053
|
|
|
1054
|
+
Machine-verifiable proof: mutation testing, property-based testing,
|
|
1055
|
+
invariant checking, artifact capture with SHA-256 manifests.
|
|
1056
|
+
|
|
711
1057
|
Part of the Code Factory pattern for machine-verifiable proof.
|
|
712
1058
|
EOF
|
|
713
1059
|
}
|
|
@@ -732,6 +1078,12 @@ main() {
|
|
|
732
1078
|
types)
|
|
733
1079
|
cmd_list_types
|
|
734
1080
|
;;
|
|
1081
|
+
quality-score)
|
|
1082
|
+
cmd_quality_score
|
|
1083
|
+
;;
|
|
1084
|
+
artifact)
|
|
1085
|
+
evidence_capture_artifact "$@"
|
|
1086
|
+
;;
|
|
735
1087
|
help|--help|-h)
|
|
736
1088
|
show_help
|
|
737
1089
|
;;
|