@metasession.co/devaudit-cli 0.1.12 → 0.1.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metasession.co/devaudit-cli",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "DevAudit CLI — installs, syncs, and operates the Metasession SDLC across consumer projects.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@clack/prompts": "^0.8.2",
36
- "@metasession.co/devaudit-plugin-sdk": "^0.1.12",
36
+ "@metasession.co/devaudit-plugin-sdk": "^0.1.14",
37
37
  "commander": "^12.1.0",
38
38
  "consola": "^3.2.3",
39
39
  "env-paths": "^3.0.0",
@@ -151,6 +151,15 @@ If the smoke results look wrong or a manual verification fails, click **Reject**
151
151
 
152
152
  ### Step 6: Finalize Compliance (Tracked Requirements Only)
153
153
 
154
+ > **Automated path (preferred).** Run the synced helper instead of doing this by hand — it flips the ticket Status → `RELEASED` (+ backlinks the release PR and records the sign-off), flips the matching `RTM.md` row, and `git mv`s the ticket to `approved-releases/`, then stages the changes for you to commit:
155
+ >
156
+ > ```bash
157
+ > ./scripts/close-out-release.sh REQ-XXX --release-pr <release-PR-#>
158
+ > git add -A && git commit -m "docs(compliance): close out REQ-XXX release ticket (RELEASED)" && git push origin develop
159
+ > ```
160
+ >
161
+ > It is **idempotent** (a no-op if already closed out) and, when `DEVAUDIT_API_KEY` + `DEVAUDIT_BASE_URL` are set, **refuses** unless the portal reports the release as `released` (so you can't flip the local tree ahead of the Production approval). The `close-out-release.yml` workflow runs the same script automatically when the portal marks a release released (and is `workflow_dispatch`-able for catch-up). The manual steps below are the fallback / reference for what the script does.
162
+
154
163
  ```bash
155
164
  mv compliance/pending-releases/RELEASE-TICKET-REQ-XXX.md compliance/approved-releases/
156
165
  ```
@@ -36,23 +36,30 @@ For typo fixes, formatting changes, dependency bumps, and other zero-risk chores
36
36
 
37
37
  If you're not sure whether your change is trivial, treat it as non-trivial (cheaper than discovering mid-PR that an auditor needs evidence).
38
38
 
39
- ## Automated mode (`sdlc-implementer` — pending Phase C smoke)
39
+ ## Default mode: the `sdlc-implementer` skill
40
40
 
41
- The [`sdlc-implementer`](#skills-inventory) skill has been authored (SKILL.md + 3 references on `main`, validator-clean) but hasn't yet been smoke-tested against `wawagardenbar-app`. Once Phase C completes, this entire walkthrough collapses to:
41
+ The [`sdlc-implementer`](#skills-inventory) skill is the **default way to implement a tracked change** it is shipped and synced into this repo at `.claude/skills/sdlc-implementer/`. Give it one GitHub issue and the whole walkthrough below collapses to:
42
42
 
43
43
  ```text
44
44
  > Implement issue #N under the SDLC.
45
45
  ```
46
46
 
47
- The skill runs phases 1–4 unattended (with a plan-approval pause for HIGH/CRITICAL risk) and surfaces a UAT review waiting for you on the portal. Approve it on the portal, then:
47
+ It runs Phases 1–4 unattended (with a plan-approval pause for HIGH/CRITICAL risk, or always-on via `--require-plan-approval`): classify risk, assign the next `REQ-XXX`, write the implementation plan, update `RTM.md`, implement, delegate all end-to-end / visual test work to [`e2e-test-engineer`](#skills-inventory), run the gates, compile evidence, open the PR, and submit for UAT review on the portal. It then **halts** at the UAT gate. After a reviewer approves on the portal:
48
48
 
49
49
  ```text
50
50
  > Resume REQ-XXX.
51
51
  ```
52
52
 
53
- The skill completes phase 5: merge, monitor post-deploy, capture production smoke evidence, mark the release Released. If changes are requested at UAT instead of approval, the skill addresses them and re-submits for UAT re-review.
53
+ It runs Phase 5: merge, monitor post-deploy, confirm production smoke evidence, advance the release. If changes are requested at UAT instead of approval, it addresses them and re-submits for UAT re-review. It **refuses** issues that decompose into multiple requirements (split them first).
54
54
 
55
- The manual walkthrough below remains the source of truth for what the skill is doing (and the fallback for cases where the skill can't be used). Until the skill ships, follow the walkthrough manually — the sample prompts at the end of this doc are the per-stage stopgap.
55
+ **When it is NOT used:**
56
+
57
+ - **Trivial / housekeeping changes** — see the escape hatch above. Docs, formatting, dependency bumps, CI tweaks (`docs:` / `chore:` / `ci:` …) don't need a requirement and don't go through the skill. (Note: `feat` / `fix` / `refactor` / `perf` commits **do** require a `[REQ-XXX]` / `Ref: REQ-XXX` and are rejected by commitlint + `validate-commits.sh` without one.)
58
+ - **Stage-1 planning in isolation, or e2e test work alone** — run the manual walkthrough / invoke `e2e-test-engineer` directly.
59
+ - **Cross-issue refactors** spanning multiple `REQ-XXX` scopes — out of the one-issue contract.
60
+ - **When the orchestration can't apply** (unusual repo state, partial work mid-stream) — fall back to the manual walkthrough below.
61
+
62
+ The manual walkthrough below is the **operational reference** for exactly what the skill does at each stage — and the fallback when the skill isn't the right fit. (For an audience-level walkthrough with sample AI prompts, see the portal's [`implementing-an-sdlc-issue.md`](https://github.com/metasession-dev/devaudit/blob/main/docs/implementing-an-sdlc-issue.md).)
56
63
 
57
64
  ---
58
65
 
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env bash
2
+ # close-out-release.sh — Reconcile the local compliance tree after a release
3
+ # is marked `released` on the DevAudit portal.
4
+ #
5
+ # For one requirement it: flips the release ticket Status -> RELEASED (backlinks
6
+ # the release PR + records the sign-off), flips the matching compliance/RTM.md
7
+ # row -> RELEASED, and moves the ticket from compliance/pending-releases/ to
8
+ # compliance/approved-releases/. It stages the changes but does NOT commit — the
9
+ # caller (the close-out workflow, or a human) commits/opens the PR.
10
+ #
11
+ # Usage:
12
+ # ./scripts/close-out-release.sh <REQ-ID> [--release-pr <url-or-number>]
13
+ #
14
+ # Example:
15
+ # ./scripts/close-out-release.sh REQ-046 --release-pr 138
16
+ #
17
+ # Optional environment (portal safety check — recommended in CI):
18
+ # DEVAUDIT_API_KEY + DEVAUDIT_BASE_URL — when both are set, the script
19
+ # confirms the portal reports the release as `released` before flipping
20
+ # anything, refusing otherwise (prevents local "RELEASED" while the portal
21
+ # is still at prod_review). When unset, it warns and proceeds (manual mode).
22
+ #
23
+ # Idempotent: if the ticket is already in approved-releases/ with Status
24
+ # RELEASED (and the RTM row already RELEASED), it exits 0 as a no-op.
25
+
26
+ set -euo pipefail
27
+
28
+ REQ_ID="${1:-}"
29
+ RELEASE_PR=""
30
+ shift || true
31
+ while [ $# -gt 0 ]; do
32
+ case "$1" in
33
+ --release-pr) RELEASE_PR="${2:-}"; shift 2 ;;
34
+ *) echo "Unknown option: $1" >&2; exit 2 ;;
35
+ esac
36
+ done
37
+
38
+ if ! printf '%s' "$REQ_ID" | grep -qE '^REQ-[0-9]{3,}$'; then
39
+ echo "Usage: $0 <REQ-ID> [--release-pr <url-or-number>] (REQ-ID like REQ-046)" >&2
40
+ exit 2
41
+ fi
42
+
43
+ PENDING="compliance/pending-releases/RELEASE-TICKET-${REQ_ID}.md"
44
+ APPROVED_DIR="compliance/approved-releases"
45
+ APPROVED="${APPROVED_DIR}/RELEASE-TICKET-${REQ_ID}.md"
46
+ RTM="compliance/RTM.md"
47
+ TODAY="$(date +%Y-%m-%d)"
48
+
49
+ # ── Optional portal safety check ─────────────────────────────────────────────
50
+ if [ -n "${DEVAUDIT_API_KEY:-}" ] && [ -n "${DEVAUDIT_BASE_URL:-}" ]; then
51
+ BASE="${DEVAUDIT_BASE_URL%/}"
52
+ SLUG="$(jq -r '.devaudit.project_slug // .project_slug // empty' sdlc-config.json 2>/dev/null || true)"
53
+ STATUS="$(curl -s -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
54
+ "${BASE}/api/ci/releases/resolve?projectSlug=${SLUG}&versionPrefix=${REQ_ID}" 2>/dev/null \
55
+ | jq -r '.latest.status // empty' 2>/dev/null || true)"
56
+ if [ -n "$STATUS" ] && [ "$STATUS" != "released" ]; then
57
+ echo "::error::Portal reports ${REQ_ID} as '${STATUS}', not 'released'. Refusing to close out." >&2
58
+ exit 1
59
+ fi
60
+ [ "$STATUS" = "released" ] && echo "Portal confirms ${REQ_ID} is released."
61
+ else
62
+ echo "::warning::DEVAUDIT_API_KEY/DEVAUDIT_BASE_URL not set — skipping portal status check (manual mode)."
63
+ fi
64
+
65
+ # ── Idempotency ──────────────────────────────────────────────────────────────
66
+ if [ ! -f "$PENDING" ] && [ -f "$APPROVED" ]; then
67
+ if grep -qE '^\*\*Status:\*\*[[:space:]]*RELEASED' "$APPROVED"; then
68
+ echo "${REQ_ID} already closed out (ticket in approved-releases/, Status RELEASED) — no-op."
69
+ exit 0
70
+ fi
71
+ fi
72
+ if [ ! -f "$PENDING" ] && [ ! -f "$APPROVED" ]; then
73
+ echo "::error::No RELEASE-TICKET-${REQ_ID}.md in pending-releases/ or approved-releases/." >&2
74
+ exit 1
75
+ fi
76
+
77
+ # ── Move ticket pending -> approved (if still pending) ───────────────────────
78
+ mkdir -p "$APPROVED_DIR"
79
+ if [ -f "$PENDING" ]; then
80
+ git mv "$PENDING" "$APPROVED" 2>/dev/null || mv "$PENDING" "$APPROVED"
81
+ echo "Moved ticket -> ${APPROVED}"
82
+ fi
83
+
84
+ # ── Flip ticket Status + backlink + sign-off (edit AFTER the move, then stage —
85
+ # avoids leaving content edits unstaged behind a rename) ────────────────────
86
+ TMP="$(mktemp)"
87
+ SIGN_OFF="**Sign-off (dual-actor):** UAT approved + Production approved on the DevAudit portal (\`released\`); post-deploy production smoke evidence captured. Closed out ${TODAY}."
88
+ PR_LINE=""
89
+ if [ -n "$RELEASE_PR" ]; then
90
+ case "$RELEASE_PR" in
91
+ http*) PR_LINE="**Release PR:** ${RELEASE_PR}" ;;
92
+ *) PR_LINE="**Release PR:** #${RELEASE_PR}" ;;
93
+ esac
94
+ fi
95
+ awk -v signoff="$SIGN_OFF" -v prline="$PR_LINE" '
96
+ BEGIN { status_done=0; signoff_added=0 }
97
+ # Flip the first Status line.
98
+ /^\*\*Status:\*\*/ && status_done==0 { print "**Status:** RELEASED"; status_done=1; next }
99
+ # Replace a placeholder Release PR line if present and a PR was supplied.
100
+ /^\*\*Release PR:\*\*/ && prline!="" { print prline; next }
101
+ { print }
102
+ # After the DevAudit Release line, append the sign-off (once) if not already present.
103
+ /^\*\*DevAudit Release:\*\*/ && signoff_added==0 {
104
+ print signoff; signoff_added=1
105
+ }
106
+ ' "$APPROVED" > "$TMP"
107
+ # Only add a Release PR line if there was no existing one to replace.
108
+ if [ -n "$PR_LINE" ] && ! grep -qE '^\*\*Release PR:\*\*' "$TMP"; then
109
+ awk -v prline="$PR_LINE" '
110
+ /^\*\*DevAudit Release:\*\*/ { print prline }
111
+ { print }
112
+ ' "$TMP" > "${TMP}.2" && mv "${TMP}.2" "$TMP"
113
+ fi
114
+ mv "$TMP" "$APPROVED"
115
+ git add "$APPROVED" 2>/dev/null || true
116
+ echo "Ticket Status -> RELEASED."
117
+
118
+ # ── Flip the RTM row -> RELEASED (preserve any parenthetical note) ───────────
119
+ if [ -f "$RTM" ] && grep -qE "^\| ${REQ_ID} " "$RTM"; then
120
+ awk -v req="$REQ_ID" '
121
+ BEGIN { FS="|"; OFS="|"; statuscol=0 }
122
+ # Locate the "Status" column from the first header row that has one.
123
+ statuscol==0 {
124
+ for (i=1; i<=NF; i++) { c=$i; gsub(/^[[:space:]]+|[[:space:]]+$/, "", c); if (c=="Status") statuscol=i }
125
+ }
126
+ $0 ~ ("^\\| " req " ") && statuscol>0 {
127
+ cell=$statuscol
128
+ note=""
129
+ if (match(cell, /\(/)) note=substr(cell, RSTART) # preserve any " (requirement note)"
130
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", note)
131
+ $statuscol = (note != "" ? " RELEASED " note " " : " RELEASED ")
132
+ print; next
133
+ }
134
+ { print }
135
+ ' "$RTM" > "$TMP" && mv "$TMP" "$RTM"
136
+ git add "$RTM" 2>/dev/null || true
137
+ echo "RTM row ${REQ_ID} -> RELEASED."
138
+ else
139
+ echo "::warning::No RTM row for ${REQ_ID} in ${RTM} — skipped RTM flip."
140
+ rm -f "$TMP"
141
+ fi
142
+
143
+ echo "Close-out staged for ${REQ_ID}. Commit + open a PR to develop to land it."
@@ -0,0 +1,96 @@
1
+ # Release close-out — reconcile the local compliance tree after a release is
2
+ # marked `released` on the DevAudit portal.
3
+ #
4
+ # Generated by `devaudit install` / `devaudit update` from sdlc-config.json.
5
+ # Do not edit manually — re-run the CLI (`devaudit update`) to regenerate.
6
+ #
7
+ # Triggers:
8
+ # - repository_dispatch (type: release-closed) — sent by the DevAudit portal
9
+ # when it advances a release to `released`. client_payload: { release,
10
+ # release_pr }.
11
+ # - workflow_dispatch — manual catch-up for an already-released requirement.
12
+ #
13
+ # It runs scripts/close-out-release.sh for the requirement (flip ticket +
14
+ # RTM, move ticket to approved-releases/), then opens a PR to develop. The
15
+ # script is idempotent, so a no-op produces no PR.
16
+
17
+ name: Release Close-out
18
+
19
+ on:
20
+ workflow_dispatch:
21
+ inputs:
22
+ release:
23
+ description: 'REQ-XXX to close out (e.g. REQ-046)'
24
+ required: true
25
+ release_pr:
26
+ description: 'Release PR number or URL to backlink (optional)'
27
+ required: false
28
+ repository_dispatch:
29
+ types: [release-closed]
30
+
31
+ permissions:
32
+ contents: write
33
+ pull-requests: write
34
+
35
+ jobs:
36
+ close-out:
37
+ name: Close out release
38
+ runs-on: {{RUNNER}}
39
+ env:
40
+ GH_TOKEN: ${{ github.token }}
41
+ DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+ with:
45
+ ref: develop
46
+ fetch-depth: 0
47
+
48
+ - name: Resolve inputs
49
+ id: in
50
+ run: |
51
+ REQ="${{ github.event.inputs.release }}${{ github.event.client_payload.release }}"
52
+ PR="${{ github.event.inputs.release_pr }}${{ github.event.client_payload.release_pr }}"
53
+ if ! printf '%s' "$REQ" | grep -qE '^REQ-[0-9]{3,}$'; then
54
+ echo "::error::No valid REQ-XXX supplied (inputs.release / client_payload.release)."
55
+ exit 1
56
+ fi
57
+ echo "req=${REQ}" >> "$GITHUB_OUTPUT"
58
+ echo "pr=${PR}" >> "$GITHUB_OUTPUT"
59
+ # Portal base URL for the safety check (read from sdlc-config.json).
60
+ BASE=$(jq -r '.devaudit.base_url // empty' sdlc-config.json 2>/dev/null || true)
61
+ echo "DEVAUDIT_BASE_URL=${BASE%/}" >> "$GITHUB_ENV"
62
+
63
+ - name: Run close-out
64
+ id: closeout
65
+ run: |
66
+ chmod +x scripts/close-out-release.sh 2>/dev/null || true
67
+ ARGS="${{ steps.in.outputs.req }}"
68
+ [ -n "${{ steps.in.outputs.pr }}" ] && ARGS="${ARGS} --release-pr ${{ steps.in.outputs.pr }}"
69
+ bash scripts/close-out-release.sh ${ARGS}
70
+ if [ -n "$(git status --porcelain)" ]; then
71
+ echo "changed=true" >> "$GITHUB_OUTPUT"
72
+ else
73
+ echo "changed=false" >> "$GITHUB_OUTPUT"
74
+ echo "Nothing to close out (idempotent no-op) — no PR opened."
75
+ fi
76
+
77
+ - name: Open close-out PR
78
+ if: steps.closeout.outputs.changed == 'true'
79
+ run: |
80
+ REQ="${{ steps.in.outputs.req }}"
81
+ BRANCH="chore/close-out-${REQ}"
82
+ git config user.name "devaudit-bot"
83
+ git config user.email "devaudit-bot@users.noreply.github.com"
84
+ git checkout -b "$BRANCH"
85
+ git add -A
86
+ git commit -m "docs(compliance): close out ${REQ} release ticket (RELEASED)
87
+
88
+ Automated reconciliation after the DevAudit portal marked ${REQ} released.
89
+ Ticket Status -> RELEASED + moved to approved-releases/; RTM row -> RELEASED.
90
+
91
+ Ref: ${REQ}"
92
+ git push -u origin "$BRANCH" --force-with-lease
93
+ gh pr create --base develop --head "$BRANCH" \
94
+ --title "docs(compliance): close out ${REQ} release ticket (RELEASED)" \
95
+ --body "Automated close-out — the DevAudit portal marked **${REQ}** \`released\`. This moves the ticket to \`approved-releases/\`, flips its Status and the RTM row to \`RELEASED\`. Review + merge to develop, then promote to main." \
96
+ || echo "::warning::PR may already exist for ${BRANCH}."