@metasession.co/devaudit-cli 0.3.25 → 0.3.27

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.
@@ -250,476 +250,15 @@ jobs:
250
250
 
251
251
  - name: Upload compliance documents
252
252
  if: steps.resolve.outputs.skip != 'true' && steps.version.outputs.version != 'skip'
253
+ env:
254
+ DEVAUDIT_PROJECT_SLUG: {{PROJECT_SLUG}}
255
+ DEVAUDIT_GIT_SHA: ${{ github.sha }}
256
+ DEVAUDIT_CI_RUN_ID: ${{ github.run_id }}
257
+ DEVAUDIT_BRANCH: ${{ github.ref_name }}
258
+ DEVAUDIT_RELEASE_VERSION: ${{ steps.version.outputs.version }}
253
259
  run: |
254
- chmod +x scripts/upload-evidence.sh 2>/dev/null || true
255
- # Common flags WITHOUT --release. Each upload appends its OWN
256
- # --release so per-requirement artifacts land in their own release
257
- # record (version = REQ-ID) instead of all collapsing into whichever
258
- # single version the triggering commit derived. DevAudit #135 follow-
259
- # up: this fixes release *attribution* (not just duplication) — an
260
- # untagged docs commit must not sweep every in-scope REQ into a date
261
- # release. Note: upload-evidence.sh keeps the LAST --release seen.
262
- FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
263
- FLAGS="${FLAGS} --create-release-if-missing --environment uat --sdlc-stage 3"
264
- DERIVED_RELEASE="${{ steps.version.outputs.version }}"
265
-
266
- # Derive change_type for the bare-date (housekeeping) DERIVED_RELEASE:
267
- # the prefix of the most recent commit's subject. No-op for tracked
268
- # releases — they get per-REQ derivation in the loop below.
269
- DERIVED_CT=$(git log -1 --pretty=%s 2>/dev/null \
270
- | grep -oE '^(feat|fix|refactor|perf|chore|docs|ci|build|test|compliance|revert)' \
271
- | head -1 || true)
272
- DERIVED_META=()
273
- [ -n "$DERIVED_CT" ] && DERIVED_META+=(--change-type "$DERIVED_CT")
274
-
275
- # Upload project-level planning docs with specific evidence_type
276
- # so the portal's framework-coverage predicates attribute them
277
- # correctly (DevAudit-Installer#205). Previously all three uploaded
278
- # as compliance_document — the catch-all that satisfies no specific
279
- # clause predicate.
280
- #
281
- # - RTM.md → rtm (ISO 29119 §3.3 traceability)
282
- # - test-plan.md → test_plan (ISO 29119 §3.4 test plan)
283
- # - test-cases.md → test_cases (ISO 29119 §3.4 test cases)
284
- upload_project_doc() {
285
- local DOC="$1" EVTYPE="$2"
286
- if [ ! -f "$DOC" ]; then return 0; fi
287
- echo "Uploading: $(basename "$DOC") (${EVTYPE})"
288
- bash scripts/upload-evidence.sh \
289
- {{PROJECT_SLUG}} _compliance-docs "$EVTYPE" "$DOC" \
290
- --category planning ${FLAGS} --release "${DERIVED_RELEASE}" \
291
- "${DERIVED_META[@]}" \
292
- || echo "Warning: Failed to upload $(basename "$DOC")"
293
- }
294
- upload_project_doc compliance/RTM.md rtm
295
- upload_project_doc compliance/test-plan.md test_plan
296
- upload_project_doc compliance/test-cases.md test_cases
297
-
298
- # Project-level Test Summary Report — a hand-authored baseline
299
- # describing the project's testing posture. As of v0.1.32 this is
300
- # uploaded as `compliance_document` (NOT `test_report`) — the
301
- # per-release Test Reports gate is now satisfied by the per-REQ
302
- # `test-execution-summary.md` uploaded in the in-scope-requirements
303
- # loop below, which carries fresh release-specific data. The
304
- # project-level TSR continues to ship as a Documents-tab baseline
305
- # but no longer poses as per-release test evidence.
306
- # See DevAudit-Installer#101.
307
- if [ -f "compliance/test-summary-report.md" ]; then
308
- echo "Uploading: test-summary-report.md (compliance_document — baseline)"
309
- bash scripts/upload-evidence.sh \
310
- {{PROJECT_SLUG}} _compliance-docs compliance_document compliance/test-summary-report.md \
311
- --category planning ${FLAGS} --release "${DERIVED_RELEASE}" \
312
- "${DERIVED_META[@]}" \
313
- || echo "Warning: Failed to upload test-summary-report.md"
314
- fi
315
-
316
- # NOTE: test-summary-report.md stays as compliance_document because
317
- # it is a project-level evergreen baseline, NOT per-release test
318
- # evidence. It does not satisfy any specific clause predicate —
319
- # the per-REQ test-execution-summary.md (test_report) does that.
320
- # This is the one intentional compliance_document use for project-
321
- # level docs (DevAudit-Installer#205).
322
-
323
- # Tier 3 per-event governance docs (devaudit#370 Phase 3a, narrowed
324
- # in v0.1.39). Only periodic-review and incident-report are CI-uploaded
325
- # — both are auto-generated by other workflows (periodic-review by the
326
- # quarterly cron, incident-report by the incident-export workflow).
327
- #
328
- # Tier 1/2 governance docs (Test_Policy, AGENT, INSTRUCTIONS,
329
- # Test_Strategy, Test_Architecture, Periodic_Security_Review_Schedule,
330
- # ROPA, DPIA, AI Disclosure) are operator-uploaded via the portal
331
- # Upload Evidence form. CI auto-upload was removed because placeholder
332
- # starter content was landing as canonical evidence; the portal form
333
- # ensures the operator reviews each refresh before it counts.
334
- upload_governance() {
335
- local FILE="$1" TYPE="$2"
336
- if [ ! -f "$FILE" ]; then return 0; fi
337
- echo "Uploading governance: $(basename "$FILE") (type=${TYPE})"
338
- bash scripts/upload-evidence.sh \
339
- {{PROJECT_SLUG}} _compliance-docs "$TYPE" "$FILE" \
340
- --category planning ${FLAGS} --release "${DERIVED_RELEASE}" \
341
- "${DERIVED_META[@]}" \
342
- || echo "Warning: Failed to upload $(basename "$FILE")"
343
- }
344
- # Recognise governance docs at top-level OR under compliance/governance/
345
- # (operator's choice — both layouts are common).
346
- upload_governance compliance/periodic-review.md periodic_review
347
- upload_governance compliance/governance/periodic-review.md periodic_review
348
- frontmatter_value() {
349
- local FILE="$1" KEY="$2"
350
- awk -v key="$KEY" '
351
- NR == 1 && $0 == "---" { in_fm=1; next }
352
- in_fm && $0 == "---" { exit }
353
- in_fm && $0 ~ "^[[:space:]]*" key ":" {
354
- sub("^[[:space:]]*" key ":[[:space:]]*", "")
355
- gsub(/^["'\'' ]+|["'\'' ]+$/, "")
356
- print
357
- exit
358
- }
359
- ' "$FILE" 2>/dev/null || true
360
- }
361
-
362
- bundle_manifest_allows_source_release() {
363
- local SOURCE_RELEASE="$1" MANIFEST=""
364
- [ -n "$SOURCE_RELEASE" ] || return 1
365
- for MANIFEST in \
366
- "compliance/pending-releases/BUNDLED-CHANGES-${DERIVED_RELEASE}.json" \
367
- "compliance/approved-releases/BUNDLED-CHANGES-${DERIVED_RELEASE}.json" \
368
- "compliance/superseded-releases/BUNDLED-CHANGES-${DERIVED_RELEASE}.json"; do
369
- [ -f "$MANIFEST" ] || continue
370
- if jq -e --arg source "$SOURCE_RELEASE" '
371
- any(.members[]?;
372
- .version == $source
373
- and (
374
- .evidenceInheritancePolicy.mode == "all_eligible"
375
- or any(.evidenceInheritancePolicy.scopes[]?; . == "release" or . == "stage" or . == "approval")
376
- )
377
- )
378
- ' "$MANIFEST" >/dev/null 2>&1; then
379
- return 0
380
- fi
381
- done
382
- return 1
383
- }
384
-
385
- # Incident reports are selected by frontmatter ownership, not by
386
- # repository-wide glob attribution. Historical files may stay on disk,
387
- # but only the incident/no-incident artefact whose source release is
388
- # the current derived release is uploaded to that release. If the
389
- # current release has an explicit bundle manifest that grants
390
- # inheritance for a predecessor, upload the file to the SOURCE release
391
- # so the portal can inherit by lineage without relabelling ownership.
392
- upload_incident_report() {
393
- local FILE="$1"
394
- if [ ! -f "$FILE" ]; then return 0; fi
395
- local KIND SOURCE_RELEASE RELEASE_SCOPE SOURCE_ISSUE SEMANTIC_ID CONTENT_HASH TARGET_RELEASE
396
- KIND="$(frontmatter_value "$FILE" incident_kind)"
397
- SOURCE_RELEASE="$(frontmatter_value "$FILE" source_release)"
398
- RELEASE_SCOPE="$(frontmatter_value "$FILE" release_scope)"
399
- SOURCE_ISSUE="$(frontmatter_value "$FILE" source_issue)"
400
- SEMANTIC_ID="$(frontmatter_value "$FILE" semantic_id)"
401
- [ -n "$SEMANTIC_ID" ] || SEMANTIC_ID="$(frontmatter_value "$FILE" incident_id)"
402
-
403
- case "$KIND" in
404
- incident|no_incidents_attestation) ;;
405
- "")
406
- echo "::error::$(basename "$FILE"): missing required frontmatter incident_kind. Expected incident or no_incidents_attestation."
407
- return 1 ;;
408
- *)
409
- echo "::error::$(basename "$FILE"): invalid incident_kind '${KIND}'. Expected incident or no_incidents_attestation."
410
- return 1 ;;
411
- esac
412
- if [ "$KIND" = "no_incidents_attestation" ] && [ -z "$SOURCE_RELEASE" ]; then
413
- echo "::error::$(basename "$FILE"): nil incident reports require source_release frontmatter."
414
- return 1
415
- fi
416
- if [ "$KIND" = "incident" ]; then
417
- if [ -z "$SOURCE_RELEASE" ] && [ -z "$RELEASE_SCOPE" ]; then
418
- echo "::error::$(basename "$FILE"): incident reports require source_release or release_scope frontmatter."
419
- return 1
420
- fi
421
- if [ -z "$SOURCE_ISSUE" ]; then
422
- echo "::error::$(basename "$FILE"): incident reports require source_issue frontmatter."
423
- return 1
424
- fi
425
- fi
426
- if [ -z "$SEMANTIC_ID" ]; then
427
- echo "::error::$(basename "$FILE"): incident reports require semantic_id or incident_id frontmatter."
428
- return 1
429
- fi
430
-
431
- TARGET_RELEASE="${SOURCE_RELEASE:-$RELEASE_SCOPE}"
432
- if [ "$TARGET_RELEASE" != "$DERIVED_RELEASE" ]; then
433
- if bundle_manifest_allows_source_release "$TARGET_RELEASE"; then
434
- echo "Incident $(basename "$FILE") is inherited by ${DERIVED_RELEASE}; preserving source release ${TARGET_RELEASE}."
435
- else
436
- echo "::notice::Skipping $(basename "$FILE"): source release ${TARGET_RELEASE} is not ${DERIVED_RELEASE} and no explicit bundle manifest grants inheritance."
437
- return 0
438
- fi
439
- fi
440
-
441
- # Completeness gate — check for REPLACE markers in structured sections.
442
- # §4 Root cause, §5 Impact, §9 Sign-off (populated reports).
443
- # For nil reports, only check the Sign-off section.
444
- if grep -q '## 4\. Root cause' "$FILE" 2>/dev/null; then
445
- SECTION=$(sed -n '/^## 4\. Root cause/,/^## [0-9]/p' "$FILE")
446
- if echo "$SECTION" | grep -q 'REPLACE'; then
447
- echo "::warning::$(basename "$FILE"): §4 (Root cause) contains REPLACE markers — skipping upload. Complete the section and push to develop."
448
- return 0
449
- fi
450
- fi
451
- if grep -q '## 5\. Impact' "$FILE" 2>/dev/null; then
452
- SECTION=$(sed -n '/^## 5\. Impact/,/^## [0-9]/p' "$FILE")
453
- if echo "$SECTION" | grep -q 'REPLACE'; then
454
- echo "::warning::$(basename "$FILE"): §5 (Impact) contains REPLACE markers — skipping upload. Complete the section and push to develop."
455
- return 0
456
- fi
457
- fi
458
- if grep -q '## 9\. Sign-off' "$FILE" 2>/dev/null; then
459
- SECTION=$(sed -n '/^## 9\. Sign-off/,/^## /p' "$FILE")
460
- if echo "$SECTION" | grep -q 'REPLACE'; then
461
- echo "::warning::$(basename "$FILE"): §9 (Sign-off) contains REPLACE markers — skipping upload. Complete the sign-off and push to develop."
462
- return 0
463
- fi
464
- fi
465
- # Nil reports have a simpler sign-off section — check it too.
466
- if grep -q '## Sign-off' "$FILE" 2>/dev/null && ! grep -q '## 9\. Sign-off' "$FILE" 2>/dev/null; then
467
- SECTION=$(sed -n '/^## Sign-off/,/^## /p' "$FILE")
468
- if echo "$SECTION" | grep -q 'REPLACE'; then
469
- echo "::warning::$(basename "$FILE"): Sign-off section contains REPLACE markers — skipping upload. Complete the sign-off and push to develop."
470
- return 0
471
- fi
472
- fi
473
- CONTENT_HASH="sha256:$(sha256sum "$FILE" | awk '{print $1}')"
474
- echo "Uploading governance: $(basename "$FILE") (type=incident_report, source_release=${TARGET_RELEASE})"
475
- bash scripts/upload-evidence.sh \
476
- {{PROJECT_SLUG}} _compliance-docs incident_report "$FILE" \
477
- --category planning ${FLAGS} --release "${TARGET_RELEASE}" \
478
- --evidence-scope release \
479
- --meta-key "incident_kind=${KIND}" \
480
- --meta-key "source_release=${TARGET_RELEASE}" \
481
- --meta-key "source_issue=${SOURCE_ISSUE:-none}" \
482
- --meta-key "semantic_id=${SEMANTIC_ID}" \
483
- --meta-key "content_hash=${CONTENT_HASH}" \
484
- "${DERIVED_META[@]}" \
485
- || echo "Warning: Failed to upload $(basename "$FILE")"
486
- }
487
- shopt -s nullglob
488
- for f in compliance/incident-report*.md compliance/governance/incident-report*.md; do
489
- upload_incident_report "$f"
490
- done
491
- # DevAudit-Installer#210 §8a: nil incident reports (per-release
492
- # "no incidents" attestations). Glob alongside populated reports
493
- # and upload as incident_report evidence.
494
- for f in compliance/nil-incident-report*.md compliance/governance/nil-incident-report*.md; do
495
- upload_incident_report "$f"
496
- done
497
- shopt -u nullglob
498
-
499
- # ── Audit-log export (DevAudit-Installer#98 WS2) ──────────────
500
- # Snapshot the portal's audit log for the rolling 90-day window
501
- # and upload as `evidence_type=audit_log`. Closes three
502
- # framework-coverage clauses on every release:
503
- # - ISO27001.A.8.16 — Monitoring activities
504
- # - EUAIA.Art-12 — Record-keeping (automatic logging)
505
- # - GDPR.Art-32 — Security of processing (audit-log half)
506
- #
507
- # The portal endpoint defaults to the last 90 days when no
508
- # `since`/`until` query params are passed; omit them so the
509
- # consumer side stays zero-config. Endpoint shipped in
510
- # META-COMPLY PR #413; project-scoped API key (uploader role)
511
- # already has read access via `resolveCiUploadAuth`.
512
- AUDIT_LOG_FILE="$(mktemp -t audit-log-XXXXXX.json)"
513
- if curl -sSf -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
514
- "${DEVAUDIT_BASE_URL%/}/api/ci/projects/{{PROJECT_SLUG}}/audit-log/export" \
515
- -o "$AUDIT_LOG_FILE"; then
516
- echo "Uploading: audit-log.json (audit_log — 90-day window)"
517
- bash scripts/upload-evidence.sh \
518
- {{PROJECT_SLUG}} _compliance-docs audit_log "$AUDIT_LOG_FILE" \
519
- --category audit_log ${FLAGS} --release "${DERIVED_RELEASE}" \
520
- "${DERIVED_META[@]}" \
521
- || echo "Warning: Failed to upload audit-log.json"
522
- else
523
- # Soft-fail: an export hiccup shouldn't break the rest of the
524
- # evidence pipeline. Surfaces as a warning in the workflow log;
525
- # the framework-coverage panel will show MISSING for the three
526
- # clauses above until the next successful upload.
527
- echo "::warning::Audit-log export failed — endpoint unreachable or 4xx/5xx. Three framework-coverage clauses (ISO27001.A.8.16, EUAIA.Art-12, GDPR.Art-32 audit-log half) will stay MISSING until the next run."
528
- fi
529
- rm -f "$AUDIT_LOG_FILE"
530
-
531
- # Helper: emit `--release-title …` `--release-summary …` `--change-type …`
532
- # args for a given REQ, derived from its release-ticket metadata and
533
- # the most recent commit attributed to that REQ. Empty pair when
534
- # neither is available. Uses the shared extraction helper so both
535
- # workflow paths produce the same title/summary for the same REQ.
536
- # DevAudit-Installer#285.
537
- req_meta_args() {
538
- local REQ="$1"; local CT=""
539
- # Source the shared helper and extract canonical title + summary
540
- source scripts/extract-release-metadata.sh 2>/dev/null || true
541
- extract_release_metadata "$REQ" 2>/dev/null || true
542
- CT=$(git log --grep "\[${REQ}\]\|Ref: ${REQ}" --pretty=%s -1 2>/dev/null \
543
- | grep -oE '^(feat|fix|refactor|perf|chore|docs|ci|build|test|compliance|revert)' \
544
- | head -1 || true)
545
- # Use if/then/fi (not `[ … ] && cmd`) — under bash -eo pipefail a
546
- # trailing `[ … ] && cmd` whose test fails returns 1, and the
547
- # function inherits that exit code; calling via $(req_meta_args …)
548
- # then aborts the step silently (DevAudit-Installer#77).
549
- if [ -n "$RELEASE_TITLE" ]; then printf -- '--release-title %q ' "$RELEASE_TITLE"; fi
550
- if [ -n "$RELEASE_SUMMARY" ]; then printf -- '--release-summary %q ' "$RELEASE_SUMMARY"; fi
551
- if [ -n "$CT" ]; then printf -- '--change-type %q ' "$CT"; fi
552
- }
553
-
554
- # Upload root-level housekeeping security summaries:
555
- # compliance/security-summary-vYYYY.MM.DD.md
556
- # compliance/security-summary-vYYYY.MM.DD.N.md
557
- #
558
- # These are generated by the housekeeping stub path and belong to
559
- # the bare-date housekeeping release encoded in the filename, not
560
- # to whichever release the current commit happened to derive.
561
- shopt -s nullglob
562
- for SECSUM in compliance/security-summary-*.md; do
563
- [ -f "$SECSUM" ] || continue
564
- SECSUM_VER=$(basename "$SECSUM" .md | sed 's/^security-summary-//')
565
- if ! echo "$SECSUM_VER" | grep -qE '^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}([.][0-9]+)?$'; then
566
- echo "::warning::Skipping $(basename "$SECSUM"): filename does not encode a housekeeping release version."
567
- continue
568
- fi
569
- echo "Uploading housekeeping security summary: $(basename "$SECSUM") -> release ${SECSUM_VER}"
570
- bash scripts/upload-evidence.sh \
571
- {{PROJECT_SLUG}} _compliance-docs security_summary "$SECSUM" \
572
- --category security ${FLAGS} --release "${SECSUM_VER}" \
573
- "${DERIVED_META[@]}" \
574
- || echo "Warning: Failed to upload $(basename "$SECSUM")"
575
- done
576
- shopt -u nullglob
577
-
578
- # Upload release tickets (pending only)
579
- if [ -d "compliance/pending-releases" ]; then
580
- for TICKET in compliance/pending-releases/*.md; do
581
- [ -f "$TICKET" ] || continue
582
- # A RELEASE-TICKET-REQ-XXX.md belongs to that requirement's
583
- # release record; any other ticket rides the derived release.
584
- TICKET_BASE=$(basename "$TICKET" .md)
585
- case "$TICKET_BASE" in
586
- RELEASE-TICKET-REQ-*)
587
- TICKET_REQ="${TICKET_BASE#RELEASE-TICKET-}"
588
- TICKET_OWNER="$TICKET_REQ"; TICKET_RELEASE="$TICKET_REQ"
589
- TICKET_META_ARGS=$(req_meta_args "$TICKET_REQ") ;;
590
- *)
591
- TICKET_OWNER="_compliance-docs"; TICKET_RELEASE="$DERIVED_RELEASE"
592
- TICKET_META_ARGS="" ;;
593
- esac
594
- echo "Uploading: $(basename "$TICKET") -> release ${TICKET_RELEASE}"
595
- eval "bash scripts/upload-evidence.sh \
596
- {{PROJECT_SLUG}} \"${TICKET_OWNER}\" release_ticket \"$TICKET\" \
597
- --category release_artifact ${FLAGS} --release \"${TICKET_RELEASE}\" \
598
- ${TICKET_META_ARGS}" \
599
- || echo "Warning: Failed to upload $(basename "$TICKET")"
600
- done
601
- fi
602
-
603
- # Upload per-requirement evidence — scoped to requirements with a
604
- # pending release ticket. Without this scoping every historical
605
- # compliance/evidence/REQ-*/ folder would be re-uploaded on every
606
- # run, re-populating the release-requirement matrix with the full
607
- # project catalogue (DevAudit #135, sibling of #133).
608
- IN_SCOPE_REQS=()
609
- if [ -d compliance/pending-releases ]; then
610
- for TICKET in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
611
- [ -f "$TICKET" ] || continue
612
- REQ_ID=$(basename "$TICKET" .md | sed 's/^RELEASE-TICKET-//')
613
- # devaudit-installer#192 — skip REQs already in a terminal
614
- # release directory. See ci.yml.template for full rationale.
615
- if compgen -G "compliance/approved-releases/RELEASE-TICKET-${REQ_ID}.md" > /dev/null 2>&1 \
616
- || compgen -G "compliance/superseded-releases/RELEASE-TICKET-${REQ_ID}.md" > /dev/null 2>&1; then
617
- echo "Skipping ${REQ_ID}: already in a terminal release directory (stale pending ticket)"
618
- continue
619
- fi
620
- IN_SCOPE_REQS+=("$REQ_ID")
621
- done
622
- fi
623
-
624
- if [ ${#IN_SCOPE_REQS[@]} -eq 0 ]; then
625
- echo "No pending release tickets found — skipping per-requirement evidence upload"
626
- else
627
- echo "In-scope requirements for this release: ${IN_SCOPE_REQS[*]}"
628
- for REQ_ID in "${IN_SCOPE_REQS[@]}"; do
629
- REQ_DIR="compliance/evidence/${REQ_ID}/"
630
- if [ ! -d "$REQ_DIR" ]; then
631
- echo "Warning: pending ticket for ${REQ_ID} but no ${REQ_DIR} on disk"
632
- continue
633
- fi
634
- REQ_META_ARGS=$(req_meta_args "$REQ_ID")
635
- for ARTIFACT in "$REQ_DIR"*.md; do
636
- [ -f "$ARTIFACT" ] || continue
637
- # Per-REQ basename → (evidence_type, evidence_category) routing.
638
- # Every SDLC-expected artifact has an explicit case with a
639
- # dedicated evidence_type so the portal's framework-coverage
640
- # predicates attribute them to the correct clause. The `*)`
641
- # catch-all was removed (DevAudit-Installer#205) — unrecognized
642
- # filenames are now skipped with a warning instead of being
643
- # silently uploaded as compliance_document/planning, which
644
- # caused wrong artifacts to satisfy wrong framework clauses.
645
- #
646
- # Evidence type → framework clause mapping:
647
- #
648
- # - test-execution-summary.md → test_report
649
- # ISO 29119-3 §3.5.6 Test Completion Report.
650
- # DevAudit-Installer#101.
651
- #
652
- # - srs-alignment.md → srs_alignment
653
- # Output of requirements-aligner skill at Stage 3.
654
- # DevAudit-Installer#119.
655
- #
656
- # - architecture-decision.md → architecture_decision
657
- # ISO 27001 A.8.25 (Secure SDLC).
658
- # DevAudit-Installer#120.
659
- #
660
- # - risk-assessment.md → risk_assessment
661
- # SOC 2 CC3.2 (Risk identification).
662
- # DevAudit-Installer#121.
663
- #
664
- # - test-scope.md → test_scope
665
- # ISO 29119 §3.3 (Test scope).
666
- #
667
- # - test-plan.md → test_plan
668
- # ISO 29119 §3.4 (Test plan, per-REQ).
669
- #
670
- # - implementation-plan.md → implementation_plan
671
- # ISO 27001 A.8.25 (Secure SDLC planning).
672
- #
673
- # - security-summary.md → security_summary
674
- # ISO 27001 A.8.28 / SOC2 CC6.1.
675
- #
676
- # - ai-use-note.md → ai_use_note
677
- # EUAIA Art. 13 (Transparency).
678
- #
679
- # - ai-prompts.md → ai_prompt_log
680
- # EUAIA Art. 12 (Record-keeping).
681
- #
682
- # - ai-agent-handoff.md → ai_agent_handoff
683
- # EUAIA Art. 14 (Human oversight).
684
- #
685
- # DevAudit-Installer#146, #205.
686
- BASENAME=$(basename "$ARTIFACT")
687
- case "$BASENAME" in
688
- test-execution-summary.md|test-summary-report.md)
689
- EVTYPE=test_report; EVCAT=test_report ;;
690
- srs-alignment.md)
691
- EVTYPE=srs_alignment; EVCAT=planning ;;
692
- architecture-decision.md)
693
- EVTYPE=architecture_decision; EVCAT=planning ;;
694
- risk-assessment.md)
695
- EVTYPE=risk_assessment; EVCAT=planning ;;
696
- test-scope.md)
697
- EVTYPE=test_scope; EVCAT=planning ;;
698
- test-plan.md)
699
- EVTYPE=test_plan; EVCAT=planning ;;
700
- implementation-plan.md)
701
- EVTYPE=implementation_plan; EVCAT=planning ;;
702
- security-summary.md)
703
- EVTYPE=security_summary; EVCAT=security ;;
704
- ai-use-note.md)
705
- EVTYPE=ai_use_note; EVCAT=ai_governance ;;
706
- ai-prompts.md)
707
- EVTYPE=ai_prompt_log; EVCAT=ai_governance ;;
708
- ai-agent-handoff.md)
709
- EVTYPE=ai_agent_handoff; EVCAT=ai_governance ;;
710
- *)
711
- echo "::warning::Unrecognized artifact ${REQ_ID}/${BASENAME} — skipping upload. Add explicit routing in compliance-evidence.yml if this is a new evidence type. (DevAudit-Installer#205)"
712
- continue ;;
713
- esac
714
- echo "Uploading: ${REQ_ID}/${BASENAME} (${EVTYPE})"
715
- eval "bash scripts/upload-evidence.sh \
716
- {{PROJECT_SLUG}} \"${REQ_ID}\" ${EVTYPE} \"$ARTIFACT\" \
717
- --category ${EVCAT} ${FLAGS} --release \"${REQ_ID}\" \
718
- ${REQ_META_ARGS}" \
719
- || echo "Warning: Failed to upload ${BASENAME}"
720
- done
721
- done
722
- fi
260
+ chmod +x scripts/upload-compliance-documents.sh 2>/dev/null || true
261
+ bash scripts/upload-compliance-documents.sh
723
262
 
724
263
  - name: Summary
725
264
  run: echo "Compliance evidence uploaded for ${{ steps.version.outputs.version }}"
package/sdlc/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metasession.co/devaudit-sdlc",
3
- "version": "0.3.25",
3
+ "version": "0.3.27",
4
4
  "description": "DevAudit SDLC CLI Engine — cross-agent terminal-driven SDLC orchestrator.",
5
5
  "bin": {
6
6
  "devaudit-sdlc": "./src/bin/devaudit-sdlc.js"