bigpowers 2.76.2 → 2.77.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.
Files changed (39) hide show
  1. package/.pi/package.json +2 -2
  2. package/.pi/prompts/build-epic.md +10 -0
  3. package/.pi/prompts/commit-message.md +1 -0
  4. package/.pi/prompts/generate-allure-report.md +284 -0
  5. package/.pi/prompts/harden-vps.md +287 -0
  6. package/.pi/prompts/plan-release.md +3 -8
  7. package/.pi/prompts/plan-work.md +14 -1
  8. package/.pi/prompts/release-branch.md +3 -0
  9. package/.pi/skills/build-epic/SKILL.md +10 -0
  10. package/.pi/skills/commit-message/SKILL.md +1 -0
  11. package/.pi/skills/generate-allure-report/SKILL.md +285 -0
  12. package/.pi/skills/harden-vps/SKILL.md +288 -0
  13. package/.pi/skills/plan-release/SKILL.md +3 -8
  14. package/.pi/skills/plan-work/SKILL.md +14 -1
  15. package/.pi/skills/release-branch/SKILL.md +3 -0
  16. package/CHANGELOG.md +15 -0
  17. package/README.md +1 -1
  18. package/SKILL-INDEX.md +1 -1
  19. package/allure-results/categories.json +23 -0
  20. package/allure-results/executor.json +6 -0
  21. package/allure-results/junit-results.xml +2743 -0
  22. package/hooks/pre-tool-use.sh +5 -0
  23. package/package.json +1 -1
  24. package/scripts/generate-allure-report.sh +146 -0
  25. package/scripts/land-branch.sh +10 -0
  26. package/scripts/record-cycle-time.sh +28 -0
  27. package/scripts/sync-status-from-epics.sh +247 -24
  28. package/scripts/validate-skill-catalog.sh +1 -1
  29. package/skills/build-epic/SKILL.md +10 -0
  30. package/skills/commit-message/SKILL.md +1 -0
  31. package/skills/generate-allure-report/REFERENCE.md +243 -0
  32. package/skills/generate-allure-report/SKILL.md +40 -0
  33. package/skills/harden-vps/REFERENCE.md +179 -0
  34. package/skills/harden-vps/SKILL.md +107 -0
  35. package/skills/plan-release/SKILL.md +3 -8
  36. package/skills/plan-work/REFERENCE.md +13 -0
  37. package/skills/plan-work/SKILL.md +1 -1
  38. package/skills/release-branch/SKILL.md +3 -0
  39. package/skills-lock.json +15 -5
@@ -0,0 +1,243 @@
1
+ # generate-allure-report — Reference
2
+
3
+ ## Data Sources
4
+
5
+ The script reads five YAML sources from the project:
6
+
7
+ | Source | Path | Fields Used |
8
+ |--------|------|-------------|
9
+ | Execution status | `specs/execution-status.yaml` | `epics`, `stories`, `development_status` |
10
+ | Release plan | `specs/release-plan.yaml` | `release.version`, `release.status`, `bugs` summary |
11
+ | Epic capsules | `specs/epics/**/epic.yaml` + `-tasks.yaml` | Epic metadata, task pass/fail counts |
12
+ | Cycle times | `specs/metrics/cycle-times.yaml` | Story-level `cycle_minutes`, `bcp_per_hour`, `source` |
13
+ | Bug registry | `specs/bugs/registry.yaml` | Bug counts by status and severity |
14
+
15
+ ## Script Body
16
+
17
+ `scripts/generate-allure-report.sh`:
18
+
19
+ ```bash
20
+ #!/usr/bin/env bash
21
+ set -euo pipefail
22
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/python-env.sh"
23
+ ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || ROOT="$(dirname "${BASH_SOURCE[0]}")/.."
24
+
25
+ mkdir -p "$ROOT/allure-results"
26
+
27
+ $PYTHON - "$ROOT" <<'PY'
28
+ import json
29
+ import sys
30
+ import xml.etree.ElementTree as ET
31
+ from pathlib import Path
32
+
33
+ root = Path(sys.argv[1])
34
+ out = root / "allure-results"
35
+
36
+ # 1. Read execution-status.yaml
37
+ exec_status_file = root / "specs" / "execution-status.yaml"
38
+ release_plan_file = root / "specs" / "release-plan.yaml"
39
+ cycle_times_file = root / "specs" / "metrics" / "cycle-times.yaml"
40
+ bugs_registry_file = root / "specs" / "bugs" / "registry.yaml"
41
+
42
+ sys.path.insert(0, str(root / "scripts" / "lib"))
43
+ from simple_yaml import parse_simple_yaml
44
+
45
+ exec_status = parse_simple_yaml(exec_status_file.read_text()) if exec_status_file.exists() else {}
46
+ release_plan = parse_simple_yaml(release_plan_file.read_text()) if release_plan_file.exists() else {}
47
+ cycle_times = parse_simple_yaml(cycle_times_file.read_text()) if cycle_times_file.exists() else {"stories": []}
48
+ bugs_registry = parse_simple_yaml(bugs_registry_file.read_text()) if bugs_registry_file.exists() else {"bugs": []}
49
+
50
+ # Build cycle-times lookup
51
+ ct_lookup = {}
52
+ for ct in cycle_times.get("stories", []):
53
+ if isinstance(ct, dict):
54
+ ct_lookup[ct.get("id", "")] = ct
55
+
56
+ # 2. Build JUnit XML
57
+ stories = exec_status.get("stories", {})
58
+
59
+ # Counts for testsuite attributes
60
+ total_stories = len(stories)
61
+ incomplete = sum(1 for s in stories.values() if isinstance(s, dict) and s.get("status") != "done")
62
+
63
+ testsuite = ET.Element("testsuite", {
64
+ "name": "bigpowers-epic-progress",
65
+ "tests": str(total_stories),
66
+ "failures": str(incomplete),
67
+ "errors": "0",
68
+ "skipped": "0",
69
+ })
70
+
71
+ for story_id in sorted(stories.keys()):
72
+ story = stories[story_id]
73
+ if not isinstance(story, dict):
74
+ continue
75
+
76
+ epic_id = story.get("epic", "unknown")
77
+ title = story.get("title", story_id)
78
+ bcps = story.get("bcps", 0)
79
+ status = story.get("status", "backlog")
80
+ risk_max = story.get("risk_max", "none")
81
+ security_max = story.get("security_max", "none")
82
+
83
+ # Enrich with cycle-times data
84
+ ct_data = ct_lookup.get(story_id, {})
85
+ cycle_minutes = ct_data.get("cycle_minutes", 0)
86
+ bcp_per_hour = ct_data.get("bcp_per_hour", 0)
87
+
88
+ # Time: cycle_minutes * 60 for seconds in Allure display
89
+ time_seconds = cycle_minutes * 60.0 if cycle_minutes else 0.0
90
+
91
+ testcase = ET.SubElement(testsuite, "testcase", {
92
+ "classname": epic_id,
93
+ "name": f"{story_id}: {title}",
94
+ "time": str(round(time_seconds, 3)),
95
+ })
96
+
97
+ props = ET.SubElement(testcase, "properties")
98
+ ET.SubElement(props, "property", {"name": "risk", "value": risk_max})
99
+ ET.SubElement(props, "property", {"name": "security", "value": security_max})
100
+ ET.SubElement(props, "property", {"name": "bcps", "value": str(bcps)})
101
+ ET.SubElement(props, "property", {"name": "status", "value": status})
102
+ ET.SubElement(props, "property", {"name": "bcp_per_hour", "value": str(bcp_per_hour)})
103
+ ET.SubElement(props, "property", {"name": "lead_time_minutes", "value": str(cycle_minutes)})
104
+
105
+ if status != "done":
106
+ ET.SubElement(testcase, "failure", {
107
+ "message": f"Story {story_id} is {status} [risk={risk_max}, security={security_max}]",
108
+ "type": "StoryIncomplete"
109
+ })
110
+
111
+ tree = ET.ElementTree(testsuite)
112
+ ET.indent(tree, space=" ")
113
+ tree.write(str(out / "junit-results.xml"), encoding="utf-8", xml_declaration=True)
114
+
115
+ # 3. Build categories.json
116
+ epics = exec_status.get("epics", {})
117
+ categories = []
118
+
119
+ for epic_id in sorted(epics.keys()):
120
+ epic = epics[epic_id]
121
+ if isinstance(epic, dict) and epic.get("status") != "done":
122
+ categories.append({
123
+ "name": f"Epic: {epic.get('title', epic_id)}",
124
+ "matchedStatuses": ["failed"],
125
+ "messageRegex": f".*{epic_id}:.*"
126
+ })
127
+
128
+ categories.append({
129
+ "name": "P0 Risk",
130
+ "matchedStatuses": ["failed"],
131
+ "messageRegex": ".*risk.*P0.*"
132
+ })
133
+ categories.append({
134
+ "name": "Security Review",
135
+ "matchedStatuses": ["failed"],
136
+ "messageRegex": ".*security.*(?:medium|high).*"
137
+ })
138
+
139
+ # Add bug-based categories
140
+ bug_list = bugs_registry.get("bugs", [])
141
+ bug_count = len(bug_list) if isinstance(bug_list, list) else 0
142
+ open_bugs = sum(1 for b in bug_list if isinstance(b, dict) and b.get("status") not in ("fixed", "closed", None))
143
+ if open_bugs > 0:
144
+ categories.append({
145
+ "name": "Open Bugs",
146
+ "matchedStatuses": ["failed"],
147
+ "messageRegex": ".*Bug.*"
148
+ })
149
+
150
+ (out / "categories.json").write_text(json.dumps(categories, indent=2))
151
+
152
+ # 4. Build executor.json
153
+ rl = release_plan.get("release", {}) if isinstance(release_plan.get("release"), dict) else {}
154
+ executor = {
155
+ "name": "bigpowers",
156
+ "type": "bigpowers",
157
+ "buildName": rl.get("version", "unknown") if isinstance(rl, dict) else "unknown",
158
+ "buildOrder": len(exec_status.get("development_status", {})),
159
+ }
160
+ (out / "executor.json").write_text(json.dumps(executor, indent=2))
161
+
162
+ # Summary
163
+ epic_count = len([e for e in epics.values() if isinstance(e, dict) and e.get("status") == "done"])
164
+ total_epics = len(epics)
165
+ print(f"generate-allure-report: {total_stories} stories, {epic_count}/{total_epics} epics done, {bug_count} bugs")
166
+ print(f" -> {out}/junit-results.xml")
167
+ print(f" -> {out}/categories.json")
168
+ print(f" -> {out}/executor.json")
169
+ PY
170
+ ```
171
+
172
+ ## JUnit XML Schema
173
+
174
+ ```xml
175
+ <?xml version="1.0" encoding="utf-8"?>
176
+ <testsuite name="bigpowers-epic-progress" tests="N" failures="F" errors="0" skipped="0">
177
+ <testcase classname="e01" name="e01s01: Security slopcheck tags" time="0.75">
178
+ <properties>
179
+ <property name="risk" value="none"/>
180
+ <property name="security" value="none"/>
181
+ <property name="bcps" value="1"/>
182
+ <property name="status" value="done"/>
183
+ <property name="bcp_per_hour" value="1.3"/>
184
+ <property name="lead_time_minutes" value="45"/>
185
+ </properties>
186
+ </testcase>
187
+ <testcase classname="e01" name="e01s99: Some incomplete story" time="0.0">
188
+ <properties>...</properties>
189
+ <failure message="Story e01s99 is backlog [risk=P0, security=high]" type="StoryIncomplete"/>
190
+ </testcase>
191
+ </testsuite>
192
+ ```
193
+
194
+ ## Categories JSON Schema
195
+
196
+ ```json
197
+ [
198
+ {
199
+ "name": "Epic: Quality Core - Skill Hardening",
200
+ "matchedStatuses": ["failed"],
201
+ "messageRegex": ".*e45:.*"
202
+ },
203
+ {
204
+ "name": "P0 Risk",
205
+ "matchedStatuses": ["failed"],
206
+ "messageRegex": ".*risk.*P0.*"
207
+ },
208
+ {
209
+ "name": "Security Review",
210
+ "matchedStatuses": ["failed"],
211
+ "messageRegex": ".*security.*(?:medium|high).*"
212
+ }
213
+ ]
214
+ ```
215
+
216
+ ## Executor JSON Schema
217
+
218
+ ```json
219
+ {
220
+ "name": "bigpowers",
221
+ "type": "bigpowers",
222
+ "buildName": "2.76.2",
223
+ "buildOrder": 400
224
+ }
225
+ ```
226
+
227
+ ## Example Usage
228
+
229
+ ```bash
230
+ # Generate reports
231
+ bash scripts/generate-allure-report.sh
232
+
233
+ # Verify output
234
+ test -f allure-results/junit-results.xml && echo "JUnit OK"
235
+ test -f allure-results/categories.json && echo "Categories OK"
236
+ test -f allure-results/executor.json && echo "Executor OK"
237
+
238
+ # Serve with Allure
239
+ allure serve allure-results/
240
+
241
+ # Or open the Allure TestOps UI
242
+ allure open allure-results/
243
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: generate-allure-report
3
+ model: sonnet
4
+ effort: standard
5
+ description: "Generate Allure-ready reports from bigpowers YAML metadata. Reads execution-status.yaml, release-plan.yaml, epic capsules, task YAMLs, cycle-times.yaml, and bug registry to produce allure-results/junit-results.xml, categories.json, and executor.json. Use when preparing progress dashboards, integrating with Allure TestOps, or generating CI reports."
6
+ ---
7
+
8
+ # Generate Allure Report
9
+
10
+ Generate Allure TestOps-compatible reports from bigpowers project metadata. Produces JUnit XML for story-level test results, custom categories for filtering, and executor metadata — all in the `allure-results/` directory.
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ bash scripts/generate-allure-report.sh
16
+ ```
17
+
18
+ ## What It Produces
19
+
20
+ Three files in `allure-results/`:
21
+
22
+ | File | Description |
23
+ |------|-------------|
24
+ | `junit-results.xml` | One `<testcase>` per story with `<properties>` for risk, security, WSJF, tier, wave, and status. Incomplete stories get a `<failure>` element. |
25
+ | `categories.json` | Custom Allure categories for filtering by epic, risk level (P0), and security reviews. |
26
+ | `executor.json` | Build metadata — name, type, version from release-plan.yaml, build order. |
27
+
28
+ ## Data Sources
29
+
30
+ See [REFERENCE.md](REFERENCE.md)
31
+
32
+ ## Verify
33
+
34
+ ```bash
35
+ test -f allure-results/junit-results.xml && test -f allure-results/categories.json && test -f allure-results/executor.json
36
+ ```
37
+
38
+ ## Handoff
39
+
40
+ - next_skill: null (terminal skill — no downstream workflow step)
@@ -0,0 +1,179 @@
1
+ # Harden VPS — Reference
2
+
3
+ ## Health check script
4
+
5
+ Deploy to `/opt/bigbase/scripts/healthcheck.sh`:
6
+
7
+ ```bash
8
+ #!/bin/bash
9
+ LOG=/var/log/bigbase/health.log
10
+ mkdir -p /var/log/bigbase
11
+ DISK=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
12
+ RAM=$(free | awk '/Mem:/ {printf "%.0f", $3/$2*100}')
13
+ NOW=$(date -Iseconds)
14
+ [ "$DISK" -gt 85 ] && echo "$NOW ALERT: Disk ${DISK}%" >> "$LOG"
15
+ [ "$RAM" -gt 90 ] && echo "$NOW ALERT: RAM ${RAM}%" >> "$LOG"
16
+ systemctl is-active --quiet bigbase || { echo "$NOW ALERT: BigBase DOWN" >> "$LOG"; systemctl restart bigbase; }
17
+ [ -f /var/run/reboot-required ] && echo "$NOW WARN: kernel reboot pending" >> "$LOG"
18
+ ```
19
+
20
+ Make executable: `chmod +x /opt/bigbase/scripts/healthcheck.sh`
21
+
22
+ ## BigBase systemd unit
23
+
24
+ Full hardened unit at `/etc/systemd/system/bigbase.service`:
25
+
26
+ ```ini
27
+ [Unit]
28
+ Description=BigBase BaaS Platform
29
+ Documentation=https://github.com/danielvm-git/bigbase
30
+ After=network.target caddy.service
31
+ Wants=caddy.service
32
+
33
+ [Service]
34
+ Type=simple
35
+ User=bigbase
36
+ Group=bigbase
37
+ WorkingDirectory=/opt/bigbase
38
+ ExecStart=/opt/bigbase/bin/bigbase serve \
39
+ --port 8080 \
40
+ --db /opt/bigbase/data/bigbase.db \
41
+ --sites-domain <your-domain.example.com>
42
+ EnvironmentFile=-/opt/bigbase/.env
43
+ Environment=BIGBASE_HOME=/opt/bigbase
44
+ Environment=HOME=/opt/bigbase
45
+ Environment=NPM_CONFIG_CACHE=/opt/bigbase/.npm
46
+ Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
47
+ Restart=always
48
+ RestartSec=5
49
+ StartLimitIntervalSec=60
50
+ StartLimitBurst=3
51
+ TimeoutStopSec=30
52
+ KillSignal=SIGTERM
53
+ NoNewPrivileges=yes
54
+ PrivateTmp=yes
55
+ ProtectSystem=full
56
+ ProtectHome=yes
57
+ ProtectKernelTunables=yes
58
+ ProtectKernelModules=yes
59
+ ProtectControlGroups=yes
60
+ RestrictAddressFamilies=AF_INET AF_INET6
61
+ RestrictRealtime=yes
62
+ ReadWritePaths=/opt/bigbase/data
63
+ ReadWritePaths=/opt/bigbase/backups
64
+ ReadWritePaths=/opt/bigbase/secrets
65
+ ReadWritePaths=/opt/bigbase/.npm
66
+ ReadWritePaths=/opt/bigbase/logs
67
+ LimitNOFILE=65536
68
+
69
+ [Install]
70
+ WantedBy=multi-user.target
71
+ ```
72
+
73
+ ## fail2ban jail.local
74
+
75
+ ```ini
76
+ [sshd]
77
+ enabled = true
78
+ port = ssh
79
+ filter = sshd
80
+ logpath = /var/log/auth.log
81
+ maxretry = 3
82
+ bantime = 3600
83
+ findtime = 600
84
+ ```
85
+
86
+ Note: BigBase auth jail deferred — BigBase logs to journald, not a file. To enable, use `backend = systemd` with a journald filter.
87
+
88
+ ## contabo-snapshot.sh
89
+
90
+ Deploy to `/opt/bigbase/scripts/contabo-snapshot.sh`. Reads credentials from `/opt/bigbase/.env` (same file used by BigBase systemd unit):
91
+
92
+ ```bash
93
+ #!/bin/bash
94
+ set -e
95
+ LOG=/var/log/bigbase/snapshot.log
96
+ ENVFILE=/opt/bigbase/.env
97
+ INSTANCE_ID=<your-contabo-instance-id>
98
+
99
+ [ -f "$ENVFILE" ] || { echo "$(date -Iseconds) ERROR: $ENVFILE missing" >> "$LOG"; exit 1; }
100
+ set -a; source "$ENVFILE"; set +a
101
+
102
+ [ -z "$CONTABO_CLIENT_ID" ] && { echo "$(date -Iseconds) ERROR: CONTABO_CLIENT_ID not set" >> "$LOG"; exit 1; }
103
+
104
+ TOKEN=$(curl -s -d "client_id=$CONTABO_CLIENT_ID" \
105
+ -d "client_secret=$CONTABO_CLIENT_SECRET" \
106
+ --data-urlencode "username=$CONTABO_API_USER" \
107
+ --data-urlencode "password=$CONTABO_API_PASSWORD" \
108
+ -d 'grant_type=password' \
109
+ 'https://auth.contabo.com/auth/realms/contabo/protocol/openid-connect/token' \
110
+ | jq -r '.access_token')
111
+
112
+ [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ] && { echo "$(date -Iseconds) ERROR: auth failed" >> "$LOG"; exit 1; }
113
+
114
+ curl -s -X POST \
115
+ -H "Authorization: Bearer $TOKEN" \
116
+ -H "Content-Type: application/json" \
117
+ -H "x-request-id: $(uuidgen 2>/dev/null || echo $RANDOM)" \
118
+ "https://api.contabo.com/v1/compute/instances/${INSTANCE_ID}/snapshots" \
119
+ | tee -a "$LOG"
120
+
121
+ echo >> "$LOG"
122
+ echo "$(date -Iseconds) Snapshot requested for $INSTANCE_ID" >> "$LOG"
123
+ ```
124
+
125
+ ## Contabo credentials
126
+
127
+ Add to `/opt/bigbase/.env` (same file used by BigBase systemd unit, deployed by GitHub Actions):
128
+
129
+ ```
130
+ CONTABO_CLIENT_ID=
131
+ CONTABO_CLIENT_SECRET=
132
+ CONTABO_API_USER=
133
+ CONTABO_API_PASSWORD=
134
+ ```
135
+
136
+ Source: Contabo Customer Control Panel → API Details. ClientId and ClientSecret are generated there. API User is your email. API Password is set separately in the panel.
137
+
138
+ **Pipeline:** GitHub Secrets → deploy workflow → `/opt/bigbase/.env` on VPS. For local dev (`cntb get instances`), add to `.envrc`.
139
+
140
+ ## Contabo instance info template
141
+
142
+ Fill in your instance details from the Contabo Customer Control Panel:
143
+
144
+ ```
145
+ IP: <your-instance-ip>
146
+ IPv6: <your-instance-ipv6>
147
+ Region: <your-region>
148
+ OS: <your-os-version>
149
+ Disk: <your-disk-size>
150
+ Default user: root
151
+ Customer ID: <your-customer-id>
152
+ ```
153
+
154
+ ## BigBase monitoring alert SQL
155
+
156
+ ```sql
157
+ INSERT INTO monitoring_alerts (id, name, metric, threshold, operator, enabled, duration_seconds)
158
+ VALUES ('alert-001', 'VPS Disk above 80%', 'disk_used_percent', 80, 'gt', 1, 300);
159
+
160
+ INSERT INTO monitoring_alerts (id, name, metric, threshold, operator, enabled, duration_seconds)
161
+ VALUES ('alert-002', 'VPS CPU above 90%', 'cpu_percent', 90, 'gt', 1, 60);
162
+
163
+ INSERT INTO monitoring_alerts (id, name, metric, threshold, operator, enabled, duration_seconds)
164
+ VALUES ('alert-003', 'VPS RAM above 85%', 'mem_used_percent', 85, 'gt', 1, 120);
165
+ ```
166
+
167
+ Alerts are loaded at BigBase startup. Restart with `systemctl restart bigbase` after inserting.
168
+
169
+ ## Base64 encoding workaround
170
+
171
+ When sending scripts through Orca terminal `--text`, the local bash shell interprets `$`, `(`, and `%`. Encode locally and decode remotely:
172
+
173
+ ```bash
174
+ # Local
175
+ cat script.sh | base64
176
+
177
+ # Remote terminal
178
+ echo '<base64-output>' | base64 -d > script.sh
179
+ ```
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: harden-vps
3
+ description: "Harden a production Linux VPS for BigBase across three layers — BigBase app (systemd hardening, monitoring alerts, backup automation), Ubuntu OS (UFW firewall, fail2ban SSH, unattended-upgrades, SSH hardening), and Contabo VPS (health checks, daily backups, monthly snapshots). Use when the user wants to secure a production server, harden a VPS, audit server security, or mentions production hardening, VPS security, or harden the server."
4
+ model: haiku
5
+ effort: standard
6
+ ---
7
+
8
+ # Harden VPS
9
+
10
+ Three-layer production hardening for BigBase on Contabo VPS. Each layer independently verifiable. Apply OS first (firewall blocks attacks immediately), then BigBase (alerts + backups), then Contabo (snapshots). See [REFERENCE.md](REFERENCE.md) for full script bodies, systemd unit template, and gotchas.
11
+
12
+ ## Quick start
13
+
14
+ SSH as root into the VPS. Find credentials in the Contabo Customer Control Panel.
15
+
16
+ > **HARD GATE** — Run `ufw status` first. No firewall = layer 1 takes priority over everything.
17
+
18
+ ## Layer 1 — Ubuntu OS
19
+
20
+ ```bash
21
+ # UFW
22
+ ufw default deny incoming && ufw default allow outgoing
23
+ ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable
24
+ # → verify: ufw status | grep -q active
25
+
26
+ # fail2ban
27
+ apt install -y fail2ban
28
+ # Configure /etc/fail2ban/jail.local: sshd, maxretry=3, bantime=3600, findtime=600
29
+ systemctl restart fail2ban
30
+ # → verify: fail2ban-client status sshd
31
+
32
+ # unattended-upgrades
33
+ apt install -y unattended-upgrades && dpkg-reconfigure -plow unattended-upgrades
34
+ # → verify: systemctl is-active unattended-upgrades | grep -q active
35
+
36
+ # SSH: PermitRootLogin no, PasswordAuthentication no, PubkeyAuthentication yes
37
+ # → verify: sshd -T | grep -E 'permitrootlogin no|passwordauthentication no'
38
+
39
+ # Deploy healthcheck.sh → /opt/bigbase/scripts/healthcheck.sh
40
+ # Crontab: */5 * * * * /opt/bigbase/scripts/healthcheck.sh
41
+ ```
42
+
43
+ ## Layer 2 — BigBase application
44
+
45
+ ```bash
46
+ # systemd: User=bigbase, NoNewPrivileges=yes, ProtectSystem=full,
47
+ # ProtectKernelTunables=yes, ProtectKernelModules=yes,
48
+ # ProtectControlGroups=yes, RestrictAddressFamilies=AF_INET AF_INET6,
49
+ # RestrictRealtime=yes, PrivateTmp=yes, LimitNOFILE=65536
50
+ # → verify: systemctl show bigbase -p NoNewPrivileges -p ProtectSystem -p User
51
+
52
+ # Alerts (BigBase requires auth; insert via SQLite)
53
+ sqlite3 /opt/bigbase/data/bigbase.db "
54
+ INSERT INTO monitoring_alerts (id,name,metric,threshold,operator,enabled,duration_seconds)
55
+ VALUES ('a1','Disk >80%','disk_used_percent',80,'gt',1,300);
56
+ INSERT INTO monitoring_alerts (id,name,metric,threshold,operator,enabled,duration_seconds)
57
+ VALUES ('a2','CPU >90%','cpu_percent',90,'gt',1,60);
58
+ INSERT INTO monitoring_alerts (id,name,metric,threshold,operator,enabled,duration_seconds)
59
+ VALUES ('a3','RAM >85%','mem_used_percent',85,'gt',1,120);
60
+ "
61
+ systemctl restart bigbase
62
+
63
+ # Backup crontab (root):
64
+ # 0 2 * * * cp /opt/bigbase/data/bigbase.db /backup/bigbase-$(date +\%Y\%m\%d).db
65
+ # 0 3 * * * find /backup/ -name "bigbase-*.db" -mtime +90 -delete
66
+ ```
67
+
68
+ ## Layer 3 — Contabo VPS
69
+
70
+ ```bash
71
+ # cntb CLI
72
+ curl -sL "$(curl -sL https://api.github.com/repos/contabo/cntb/releases/latest \
73
+ | grep browser_download_url.*linux_amd64.tar.gz | head -1 | cut -d'"' -f4)" \
74
+ | tar xz -C /usr/local/bin
75
+
76
+ # Snapshot script → /opt/bigbase/scripts/contabo-snapshot.sh (reads from /opt/bigbase/.env)
77
+ # Credentials as env vars in /opt/bigbase/.env (deployed by GitHub Actions):
78
+ # CONTABO_CLIENT_ID, CONTABO_CLIENT_SECRET, CONTABO_API_USER, CONTABO_API_PASSWORD
79
+ # Crontab: 0 4 1 * * /opt/bigbase/scripts/contabo-snapshot.sh
80
+
81
+ # > HARD GATE — Snapshot cron silently fails until env vars are set in .env.
82
+ # Credentials source: Contabo Customer Panel → API Details.
83
+ # Local dev: add to .envrc. Production: GitHub Secrets → deploy → /opt/bigbase/.env
84
+ ```
85
+
86
+ ## CRITICAL GOTCHAS
87
+
88
+ 1. **Shell escaping in Orca terminals:** `$VAR`, `$(…)`, and `%` get eaten by the local shell. Always use base64: `echo '<base64>' | base64 -d > script.sh`
89
+ 2. **Crontab `%`:** cron interprets `%` as newline. Escape as `\%` in `$(date +\%Y\%m\%d)`
90
+ 3. **fail2ban exit 255:** means a jail references a missing log file. Remove the broken jail, restart.
91
+ 4. **BigBase alerts need auth:** POST to `/api/monitoring/alerts` requires Bearer token. Workaround: insert directly into SQLite, then restart BigBase.
92
+
93
+ ## Verify all 8 gates
94
+
95
+ ```bash
96
+ ufw status|grep -q active||echo FAIL:ufw
97
+ fail2ban-client status sshd>/dev/null 2>&1||echo FAIL:fail2ban
98
+ systemctl is-active unattended-upgrades|grep -q active||echo FAIL:unattended
99
+ sshd -T|grep -q 'permitrootlogin no'||echo FAIL:sshd
100
+ systemctl show bigbase -p NoNewPrivileges|grep -q yes||echo FAIL:systemd
101
+ systemctl is-active bigbase|grep -q active||echo FAIL:bigbase
102
+ sqlite3 /opt/bigbase/data/bigbase.db "SELECT count(*) FROM monitoring_alerts"|grep -q 3||echo FAIL:alerts
103
+ crontab -l|grep -q healthcheck&&crontab -l|grep -q bigbase.db&&crontab -l|grep -q contabo-snapshot||echo FAIL:crontab
104
+ echo ALL 8 GATES PASSED
105
+ ```
106
+
107
+ → verify: run the 8-gate one-liner on the VPS.
@@ -38,7 +38,6 @@ specs/epics/e01-auth-system/
38
38
  **Rationale:** Capsule dirs achieve change isolation (C9), enable archive pruning (C2/C6), and enforce SRP by decoupling spec `.md` from execution `-tasks.yaml` (C1).
39
39
 
40
40
  ## Process
41
-
42
41
  ### 1. Draft epics and stories
43
42
 
44
43
  From the conversation context, define:
@@ -48,15 +47,12 @@ From the conversation context, define:
48
47
  WSJF-sort epics: score = (Business Value + Time Criticality + Risk Reduction) / Job Size. Highest score first.
49
48
 
50
49
  > **Security risk boost:** If an epic's `specs/security/epics/<id>/THREAT_MODEL.md` identifies HIGH or CRITICAL risk, add +2 to the WSJF numerator (BV + TC + RR + 2) to reflect the urgency of addressing security concerns before they ship. Document the boost in the epic's note field in release-plan.yaml.
51
-
52
50
  ### 2. Write acceptance criteria (Gherkin)
53
51
 
54
52
  For each story, write at least one happy-path and one edge-case scenario (countable format §17 if maturity ≥ 3).
55
-
56
53
  ### 3. Write tasks with verify commands
57
54
 
58
55
  Every task must have a `verify:` command. No verify command = not a task.
59
-
60
56
  ### 4. Save specs/release-plan.yaml
61
57
 
62
58
  > **Do NOT hand-track the real version.** semantic-release decides it at merge. `version` here is a
@@ -80,7 +76,6 @@ epics:
80
76
  wsjf: 3.8
81
77
  capsule_dir: epics/e02-user-profile
82
78
  ```
83
-
84
79
  ### 5. Save epic manifest (`epic.yaml`)
85
80
 
86
81
  Each epic capsule directory contains an `epic.yaml` manifest:
@@ -105,11 +100,9 @@ stories:
105
100
  spec: e01s02-jwt.md
106
101
  tasks: e01s02-tasks.yaml
107
102
  ```
108
-
109
103
  ### 6. Save story specs (countable-story-format .md)
110
104
 
111
105
  Each story becomes a standalone `.md` file following [countable-story-format.md](file:///Users/danielvm/Developer/bigpowers/countable-story-format.md). Minimum: maturity 3 (Countable) with all 20 sections present. Acceptance criteria in §17 use Gherkin scenarios.
112
-
113
106
  ### 7. Save decoupled task files (`-tasks.yaml`)
114
107
 
115
108
  Each story has a decoupled `-tasks.yaml` with implementation steps:
@@ -133,6 +126,9 @@ tasks:
133
126
  > **HARD GATE** — Every task MUST have a runnable `verify:` command. No `verify:` = not a task.
134
127
 
135
128
  → verify: `bash scripts/validate-specs-yaml.sh`
129
+ ### 7b. Generate bug registry summary
130
+
131
+ Read `specs/bugs/registry.yaml` and add a `bugs:` section to `release-plan.yaml` with totals by status (`fixed`, `deferred`, `wontfix`, `open`): `bugs: { total: N, fixed: N, deferred: N, wontfix: N, registry: specs/bugs/registry.yaml }`.
136
132
 
137
133
  ### 8. Sync execution status
138
134
 
@@ -142,7 +138,6 @@ bash scripts/sync-status-from-epics.sh
142
138
  ### 9. Snapshot on planning close (optional)
143
139
 
144
140
  Copy to `specs/product/snapshots/release-<version>/` when the user approves the plan.
145
-
146
141
  ### 10. Suggest next steps
147
142
 
148
143
  - Run `assess-impact` before `plan-work` for any story touching existing modules.
@@ -30,8 +30,21 @@ tasks:
30
30
  verify: "npm test -- login-form.test.tsx"
31
31
  risk: P1
32
32
  status: failing # flip to passing only after verify exits 0 (e45s06)
33
+ allure:
34
+ severity: high # P0→critical, P1→high, P2→normal, P3→minor
35
+ categories:
36
+ - "Auth"
37
+ - "Security Review"
33
38
  ```
34
39
 
40
+ **Allure severity mapping:**
41
+ - `P0` → `critical`
42
+ - `P1` → `high`
43
+ - `P2` → `normal`
44
+ - `P3` → `minor`
45
+
46
+ `categories` is a list of relevant tags — wave names, test categories (e.g. `"unit"`, `"integration"`), or thematic groupings (e.g. `"Security Review"`).
47
+
35
48
  Update `specs/epics/<capsule>/epic.yaml` manifest to list the story and its BCPs. Run `bash scripts/sync-status-from-epics.sh` after structural changes.
36
49
 
37
50
  ## Plan template
@@ -51,7 +51,7 @@ If this plan touches an existing module, run `assess-impact` first to understand
51
51
 
52
52
  2. **Draft steps** — Break implementation into the smallest possible steps where each step leaves the codebase working, has one observable outcome, and can be verified with a single command. Red-flag check: name any rationalization you caught before moving to step 3.
53
53
 
54
- 3. **Write capsule story spec + tasks** — Output two files inside the active epic capsule. See [REFERENCE.md](REFERENCE.md) for file formats and the plan-template. Each task MUST include a `risk:` field (`P0` | `P1` | `P2` | `P3`) based on BCP + story type heuristics (see REFERENCE.md). If a test plan artifact (`specs/tech-architecture/eNN-TEST_PLAN_LATEST.md`) exists, read it and inherit its P0/P1 risk classifications and scenario IDs (`SC-eNNsYY-P0-NN`). Each task optionally includes a `security:` field (`none` / `low` / `medium` / `high`) sourced from the epic's `specs/security/epics/<id>/THREAT_MODEL.md`. Tasks with `security: medium` or `security: high` MUST include "no new security findings in affected paths" in their verify steps.
54
+ 3. **Write capsule story spec + tasks** — Output two files inside the active epic capsule. See [REFERENCE.md](REFERENCE.md) for file formats and the plan-template. Each task MUST include a `risk:` field (`P0` | `P1` | `P2` | `P3`) based on BCP + story type heuristics (see REFERENCE.md). If a test plan artifact (`specs/tech-architecture/eNN-TEST_PLAN_LATEST.md`) exists, read it and inherit its P0/P1 risk classifications and scenario IDs (`SC-eNNsYY-P0-NN`). Each task optionally includes a `security:` field (`none` / `low` / `medium` / `high`) sourced from the epic's `specs/security/epics/<id>/THREAT_MODEL.md`. Tasks with `security: medium` or `security: high` MUST include "no new security findings in affected paths" in their verify steps. Each task should also include an `allure:` block to drive test reporting: `severity:` maps risk to Allure severity (`P0→critical`, `P1→high`, `P2→normal`, `P3→minor`), and `categories:` is a list of relevant tags like `"Security Review"`, wave names, or test categories (see REFERENCE.md for the full template).
55
55
 
56
56
  **Requirement delta tags (e45s29):** When a story modifies existing behavior, the story spec § Requirements MUST use OpenSpec-style delta tags with mandatory before/after content:
57
57
 
@@ -43,9 +43,12 @@ If unsure, prefer **solo-local**.
43
43
  ```bash
44
44
  <full test command> && <typecheck command> && <lint command>
45
45
  git log main...HEAD --oneline | grep -vE "^[a-f0-9]+ (feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?: .+$" && echo "❌ Non-conventional commits found" || echo "✅ Commits verified"
46
+ # Block AI agent attribution (P1)
47
+ git log main...HEAD --format="%B" | grep -qiE 'co[- ]authored[- ]by' && echo "❌ Co-authored-by footer found — blocked" || echo "✅ No AI attribution"
46
48
  ```
47
49
 
48
50
  - [ ] All tests pass, no type errors, no lint violations, all commits follow Conventional Commits
51
+ - [ ] **NO `Co-authored-by` or `Co-Authored-By`** in any commit body — P1 rule (CONVENTIONS.md § Git Attribution). `land-branch.sh` blocks the merge if found.
49
52
 
50
53
  ### 2. Coverage check
51
54