@mhosaic/feedback-cli 0.24.0 → 0.26.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mhosaic/feedback-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "CLI to install @mhosaic/feedback into a host app, verify the integration, and drop a guided Claude Code skill (/integrate-feedback) into ~/.claude/skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,24 +30,31 @@ Load via `ToolSearch`: `feedback_get`, `feedback_comment`, `feedback_update`.
|
|
|
30
30
|
|
|
31
31
|
1. `feedback_get <report-id>` to confirm the report exists and is in a state where close/reopen makes sense:
|
|
32
32
|
- `awaiting_validation` is the expected state. If status is anything else (`new`, `in_progress`, `closed`, `rejected`, `duplicate`, `wontfix`), surface the current state to the user and ask whether to proceed.
|
|
33
|
-
2.
|
|
33
|
+
2. Detect the operator's display name for attribution: `git config user.name`, falling back to `$USER`. Call it `<operator>` below — it goes into every `feedback_update` call as `actor_label` so notifications say "fermé par <operator>" instead of a generic team label.
|
|
34
|
+
3. **`yes` case** (submitter validated):
|
|
35
|
+
|
|
34
36
|
- `feedback_comment` with the templated body:
|
|
35
37
|
|
|
36
38
|
```
|
|
37
39
|
Validated by submitter: fix is working as expected.
|
|
38
40
|
<operator-note (verbatim, if provided)>
|
|
39
41
|
```
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
|
|
43
|
+
- `feedback_update status=closed note="<operator-note-or-empty>" actor_label="<operator>"`.
|
|
44
|
+
|
|
45
|
+
4. **`no` case** (submitter rejected):
|
|
46
|
+
|
|
42
47
|
- `feedback_comment` with the templated body:
|
|
43
48
|
|
|
44
49
|
```
|
|
45
50
|
Submitter reports the fix did not resolve the issue.
|
|
46
51
|
<operator-note (verbatim, if provided)>
|
|
47
52
|
```
|
|
48
|
-
|
|
53
|
+
|
|
54
|
+
- `feedback_update status=in_progress note="Reopened after submitter rejection." actor_label="<operator>"` so it appears again on the next `/feedback-pull`.
|
|
49
55
|
- **Do not** touch the existing `fix_branch`. If the PR was already merged, the next attempt will branch fresh from staging. If the PR is still open, the operator decides whether to close it.
|
|
50
|
-
|
|
56
|
+
|
|
57
|
+
5. Summarize for the user: report id, old status → new status, operator note (if any), one-line confirmation.
|
|
51
58
|
|
|
52
59
|
## Safety rules
|
|
53
60
|
|
|
@@ -98,8 +98,8 @@ An Issue is an auto-detected, deduped server-log error — there is no human sub
|
|
|
98
98
|
10. `git push -u origin feedback/<branch>`.
|
|
99
99
|
11. `gh pr create --base <base-branch> --head feedback/<branch> --title "..." --body "..."`. Body must include: Summary, Refs report ID, Test plan checklist. Don't add "🤖 Generated with Claude" footers; the body should read like a normal teammate PR.
|
|
100
100
|
12. `fix_branch_create` with `name=feedback/<branch>`, `project_slug`, `report_ids=[<report-id>]`, `plan=<one-paragraph plan>`. Then `fix_branch_update` with `head_sha=<short-sha>` and `status=awaiting_validation` (the PR is open and waiting for review + staging validation).
|
|
101
|
-
13. `feedback_comment` on the report with the templated state-fact (see Comments policy). Author label: `Claude
|
|
102
|
-
14. `feedback_update status=awaiting_validation` with `note="PR #<n> open against <base>."
|
|
101
|
+
13. `feedback_comment` on the report with the templated state-fact (see Comments policy). Author label: `Claude (via <operator>)` where `<operator>` is detected via `git config user.name`, falling back to `$USER`.
|
|
102
|
+
14. `feedback_update status=awaiting_validation` with `note="PR #<n> open against <base>."` and `actor_label="<operator>"` (same operator name detected for the comment's author label in step 13 — `git config user.name`, falling back to `$USER`).
|
|
103
103
|
15. Return to the original branch with `git switch -` (or `git switch <base-branch>`). Confirm working tree is clean.
|
|
104
104
|
16. Summarize for the user: report ID, branch, PR link, fix_branch ID, what changed in one sentence.
|
|
105
105
|
|
|
@@ -55,11 +55,15 @@ The project's `allowed_origins` doesn't include the URL the request comes from.
|
|
|
55
55
|
- Internal: use existing companies (`mhosaic`, `arime`, `norag`); create a new project under one. Needs Feedback group + Membership.
|
|
56
56
|
- External: create a brand-new Company. Needs `is_staff=True`. The client can't log into software-factory (it's @mhosaic.com-only SSO); Mhosaic acts as report-viewer for v1.
|
|
57
57
|
|
|
58
|
-
**"Do clients get
|
|
59
|
-
|
|
58
|
+
**"Do clients get notifications / digests?"**
|
|
59
|
+
Notifications go to a per-project Google Chat space (new reports, incidents, ✅ closures, Friday weekly digest) — wired by pasting the space's incoming-webhook URL into the project settings ("Notifications Google Chat"). **A project with no webhook falls through to the platform's global room: the client sees nothing.** Wiring it is part of the operator phase (Step 4.5 of `references/operator-provision.md`). Email digests additionally require SMTP env vars on the backend — ask the platform operator.
|
|
60
|
+
|
|
61
|
+
**"Why are the project's Métriques / Journaux tabs empty?"**
|
|
62
|
+
`observability enabled` alone forwards nothing. Metrics need `ObservabilityTarget` rows (POST `targets` to `/projects/<id>/observability/enable/` — operator Step 4.6a) and logs need the `log_destinations` block in the client app's DO spec (`scripts/observability/forward-app-logs.sh` — Step 4.6b, triggers a redeploy). If either was skipped at provisioning time, run Step 4.6 now; both are idempotent.
|
|
60
63
|
|
|
61
64
|
**"How do I also enable the QA Meter?"**
|
|
62
65
|
The QA Meter is an opt-in second FAB (half size, stacked above the feedback button) that shows test-coverage status. It's host-served and host-gated — no backend involvement. Build the artifact with `mhosaic-feedback qa refresh`, serve `qa-status.json` as a static asset (or from your own endpoint), then enable it per surface:
|
|
66
|
+
|
|
63
67
|
- **npm / React provider:** `createFeedback({ …, qaMeter: { source: '/qa-status.json' } })` — `<FeedbackProvider>` forwards the `qaMeter` prop automatically.
|
|
64
68
|
- **CDN / loader embed:** add `data-qa-source="/qa-status.json"` to the `<script>` tag (optional `data-qa-locale`).
|
|
65
69
|
|
|
@@ -105,18 +109,20 @@ Stop. Don't continue.
|
|
|
105
109
|
|
|
106
110
|
## Operator phase
|
|
107
111
|
|
|
108
|
-
Read **`references/operator-provision.md`** and follow it step by step
|
|
112
|
+
Read **`references/operator-provision.md`** and follow it step by step. The phase is **A→Z**: a project leaves this flow with its full 360 view (widget + Chat alerts + metrics + logs + members) or with each gap recorded as an explicit operator decision — never a silent skip.
|
|
109
113
|
|
|
110
114
|
1. Confirm operator is SSO-logged-in to software-factory in Chrome.
|
|
111
115
|
2. Collect: company (existing or new), project name + slug, allowed origins, `share_reports_with_widget`.
|
|
112
|
-
3. Drive Chrome MCP to the admin SPA, refresh the JWT via `/api/auth/token/refresh/`, call `POST /companies/`, `POST /projects/`, `POST /project-keys/create/` from the in-tab `fetch()` (uses the existing cookies).
|
|
116
|
+
3. Drive Chrome MCP to the admin SPA, refresh the JWT via `/api/auth/token/refresh/`, call `POST /companies/`, `POST /projects/`, `POST /project-keys/create/` from the in-tab `fetch()` (uses the existing cookies). Company create auto-grants the creator an owner Membership.
|
|
113
117
|
4. Capture the plaintext `pk_proj_…` from the keys response.
|
|
114
|
-
5.
|
|
115
|
-
6.
|
|
116
|
-
7. **(
|
|
117
|
-
8.
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
5. **Chat notifications (Step 4.5):** create or reuse the client's "«Company» Alerts" Chat space, add an incoming webhook named "Mhosaic Feedback", paste its URL into the project's settings page ("Notifications Google Chat"), fire a `[TEST]` probe and confirm the card lands. Never echo the webhook URL into the chat — it's a credential.
|
|
119
|
+
6. **Observability (Step 4.6, governance-gated):** POST `targets` (`environment` × `component` × DO app id) to `/projects/<id>/observability/enable/` so the Métriques page shows the deployment(s), then run `scripts/observability/forward-app-logs.sh <app-id> <index>` per backend app (triggers a redeploy — verify ACTIVE). Without this step the Métriques/Journaux tabs stay empty forever.
|
|
120
|
+
7. **Members (Step 4.7):** add the responsible Mhosaic owner + any client users; mint an MCP key if the fix-flow is wanted. The company must not end the flow member-less.
|
|
121
|
+
8. Verify the project + key show up in the admin SPA.
|
|
122
|
+
9. **Completeness card (Step 5):** build the "Provisioning 360" checklist; every unchecked line must carry the operator's explicit skip reason. Print it with the handoff payload.
|
|
123
|
+
10. Offer to install the skill globally so the operator can run `/integrate-feedback` in the client's repo for the consumer phase. The full offer logic is at the end of `references/operator-provision.md`.
|
|
124
|
+
|
|
125
|
+
After this phase the operator has: (a) the Provisioning 360 card + handoff payload in the chat, (b) optionally `/integrate-feedback` available globally so the next `claude` in the client repo picks it up.
|
|
120
126
|
|
|
121
127
|
---
|
|
122
128
|
|
|
@@ -161,7 +161,127 @@ The response includes `key: "pk_proj_…"` in plaintext. **This is the only time
|
|
|
161
161
|
|
|
162
162
|
---
|
|
163
163
|
|
|
164
|
-
## Step 5 —
|
|
164
|
+
## Step 4.5 — Wire Google Chat notifications (recommended)
|
|
165
|
+
|
|
166
|
+
Without a per-project webhook, the project's notifications (new reports, incidents, ✅ closures, Friday digest) fall through to the platform's **global** Chat room — the client never sees them. Wire it now, while you're already in the admin SPA:
|
|
167
|
+
|
|
168
|
+
1. **Space.** In Google Chat, create a space named `<Company> Alerts` (or reuse the company's existing alerts space — one space per company, all its projects post there). Add the Mhosaic owner(s) for this client; add client contacts only if the client should see raw alerts.
|
|
169
|
+
2. **Webhook.** Space name → _Apps & integrations_ → _Webhooks_ → add one named `Mhosaic Feedback`, avatar URL `https://software-factory-3tbbu.ondigitalocean.app/small-logo.png`. Copy the webhook URL.
|
|
170
|
+
3. **Wire it.** In the admin SPA: project → _Modifier_ → « Notifications Google Chat » → paste the URL → _Sauvegarder_. The status flips to « Webhook configuré ✓ ».
|
|
171
|
+
**The webhook URL is a credential** — anyone holding it can post to the space. Have the operator paste it directly into the SPA input (⌘V); never echo it into the conversation, the handoff payload, or a fetch() you print.
|
|
172
|
+
4. **Verify delivery.** Fire a test probe and confirm a `[TEST]` card lands in the space:
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
fetch('/api/feedback/v1/notifications/test/', {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
headers: {
|
|
178
|
+
Authorization: 'Bearer TOKEN',
|
|
179
|
+
'Content-Type': 'application/json',
|
|
180
|
+
},
|
|
181
|
+
body: JSON.stringify({ project_slug: 'project-slug' }),
|
|
182
|
+
})
|
|
183
|
+
.then((r) => r.json())
|
|
184
|
+
.then((d) => JSON.stringify(d))
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
If the operator skips this step, say so in the handoff conversation ("Chat notifications not wired — project will not alert anyone") so it's a visible decision, not a silent gap.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Step 4.6 — Wire observability: metrics targets + log forwarding
|
|
192
|
+
|
|
193
|
+
The project's Métriques and Journaux tabs are EMPTY until this step runs — `enabled=true` alone forwards nothing (the 2026-07-06 audit found three client projects "enabled" for weeks with zero data). Two sub-steps, both idempotent.
|
|
194
|
+
|
|
195
|
+
**Governance gate first** (AskUserQuestion): "Is this an internal/dogfood project, OR has a signed DPA + sub-processor disclosure been completed for this client?" Storing a client's server logs makes them data we process on their behalf (`docs/observability/data-governance.md`). If neither → record the skip for the completeness card and continue to Step 4.7; the rest of this step is off until the gate is met.
|
|
196
|
+
|
|
197
|
+
### 4.6a — Metrics targets (one API call)
|
|
198
|
+
|
|
199
|
+
1. Identify the client's DO app(s). If `doctl` is available locally, discover them:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
doctl apps list --format ID,Spec.Name | grep -i <client-name>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Confirm the mapping with the operator (AskUserQuestion if ambiguous): one target per deployment, `environment` (production/staging) × `component` (app, or frontend/backend when split). Frontend SPAs get `"metric_scan": false` — no useful DO metrics.
|
|
206
|
+
|
|
207
|
+
2. POST the targets (reuse the `TOKEN` from Step 4; requires owner/staff):
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
fetch('/api/feedback/v1/projects/PROJECT_UUID/observability/enable/', {
|
|
211
|
+
method: 'POST',
|
|
212
|
+
headers: { Authorization: 'Bearer TOKEN', 'Content-Type': 'application/json' },
|
|
213
|
+
body: JSON.stringify({
|
|
214
|
+
targets: [
|
|
215
|
+
{ environment: 'staging', component: 'app', source_app: 'DO_APP_ID' },
|
|
216
|
+
// { environment: 'production', component: 'frontend', source_app: '…', metric_scan: false },
|
|
217
|
+
],
|
|
218
|
+
}),
|
|
219
|
+
})
|
|
220
|
+
.then((r) => r.json())
|
|
221
|
+
.then((d) => JSON.stringify(d))
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Capture `index_name`, `endpoint`, and the echoed `targets` from the response. Verify in the SPA: the project's **Métriques** page must now list the deployment(s) instead of « Aucun déploiement supervisé ».
|
|
225
|
+
|
|
226
|
+
### 4.6b — Log forwarding (one script run per backend app)
|
|
227
|
+
|
|
228
|
+
If working from a checkout of `feedback-tool-mhosaic` (Mhosaic-managed apps), run the helper — it fetches the forwarder credential at runtime (never printed), preserves the app's encrypted secrets, and is a no-op when the destination already exists:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
scripts/observability/forward-app-logs.sh <DO_APP_ID> <index_name from 4.6a>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
> ⚠️ This triggers a **full redeploy** of the client app, and forwarding only activates on a **successful** deploy. Check `doctl apps list-deployments <DO_APP_ID>` shows ACTIVE before running, and confirm it returns to ACTIVE after.
|
|
235
|
+
|
|
236
|
+
If the client's DevOps manages their own DO account instead, hand them this block to add under each log-emitting service in their app spec (`doctl apps update <app-id> --spec <file>` or DO console → Settings → App Spec):
|
|
237
|
+
|
|
238
|
+
````markdown
|
|
239
|
+
### Mhosaic server-log forwarding — add to your DO app spec
|
|
240
|
+
|
|
241
|
+
```yaml
|
|
242
|
+
log_destinations:
|
|
243
|
+
- name: mhosaic-feedback
|
|
244
|
+
open_search:
|
|
245
|
+
endpoint: <endpoint from the response>
|
|
246
|
+
index_name: <index_name from the response>
|
|
247
|
+
basic_auth:
|
|
248
|
+
user: svc_log_forwarder
|
|
249
|
+
password: <get the svc_log_forwarder password from your Mhosaic operator>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The `svc_log_forwarder` credential is **write-only** (it cannot read any logs) and shared across forwarding clients — get the password from the Mhosaic operator's secret store and don't commit it. Logs appear in the Journaux tab within a few minutes of a successful deploy.
|
|
253
|
+
````
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Step 4.7 — Members: who owns this tenant
|
|
258
|
+
|
|
259
|
+
The company must not end this flow member-less — a member-less company means nobody receives digests or @-mentions, and the Compagnies page will flag it amber. The **creator already holds an owner Membership** (granted automatically on company create). On top of that:
|
|
260
|
+
|
|
261
|
+
1. Ask the operator who is responsible for this client (AskUserQuestion, suggest the usual owners). Add them via the company page's **Membres actuels** panel (add-by-email works before first SSO login — an invite materializes on their first sign-in).
|
|
262
|
+
2. If the client's own people should see the console, add them the same way (role `member`), or use a scoped grant for a single project/environment (`feedback_grant_access`) — see `docs/observability/onboarding.md` §3b.
|
|
263
|
+
3. If the company has no MCP key yet and the fix-flow (Claude Code against MCP) is wanted, mint one in the SPA (Clés MCP) — capture the `sk_proj_…` once, DM it to the responsible operator.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Step 5 — Completeness card + handoff payload
|
|
268
|
+
|
|
269
|
+
**The flow is not done until every line below is ✅ or an explicit operator decision to skip.** Build the card first; if any line is ✗ and the operator hasn't said "skip", go back and finish that step — a silent gap here is how projects end up alert-less for weeks.
|
|
270
|
+
|
|
271
|
+
```markdown
|
|
272
|
+
## Provisioning 360 — <company>/<project>
|
|
273
|
+
|
|
274
|
+
- [x] Project + widget key (`pk_proj_…`)
|
|
275
|
+
- [x] Chat: space « <Company> Alerts » + webhook `Mhosaic Feedback` + [TEST] card received
|
|
276
|
+
- [x] Observability targets: `<env>/<component>=<app-id>` (Métriques shows the deployment)
|
|
277
|
+
- [x] Log forwarding: deploy ACTIVE with `log_destinations` → `logs-<company>-<project>`
|
|
278
|
+
- [x] Members: creator owner + `<responsible@mhosaic.com>` — company not member-less
|
|
279
|
+
- [ ] MCP key: skipped (operator: "<reason>")
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Every `[ ]` line MUST carry the operator's stated reason. Include this card at the top of the handoff payload below.
|
|
283
|
+
|
|
284
|
+
Construct this Markdown block and present it to the operator as the deliverable:
|
|
165
285
|
|
|
166
286
|
Construct this Markdown block and present it to the operator as the deliverable:
|
|
167
287
|
|
|
@@ -209,56 +329,6 @@ Print this to the chat.
|
|
|
209
329
|
|
|
210
330
|
---
|
|
211
331
|
|
|
212
|
-
## Step 5.5 — (Optional, gated) enable server-log forwarding
|
|
213
|
-
|
|
214
|
-
Forwarding the client's **server logs** into Mhosaic's central store (the **Observabilité** tab) is optional and **gated**. Walk through this only if the operator wants it.
|
|
215
|
-
|
|
216
|
-
1. **Offer it** (AskUserQuestion): "Set up server-log forwarding for this project into Mhosaic observability?" If **No** → skip to Step 6.
|
|
217
|
-
|
|
218
|
-
2. **DO App Platform check** (AskUserQuestion): "Is the client app hosted on DigitalOcean App Platform?" Server-log forwarding rides DO's native `log_destinations`, so if **No** it isn't available through this flow — tell the operator and skip to Step 6. (The widget's own browser telemetry is a separate, consumer-phase concern and is unaffected.)
|
|
219
|
-
|
|
220
|
-
3. **Governance gate** (AskUserQuestion): "Is this an internal/dogfood project, OR has a signed DPA + sub-processor disclosure been completed for this client?" Storing a client's logs makes them data we process on their behalf (see `docs/observability/data-governance.md`).
|
|
221
|
-
|
|
222
|
-
- If **neither** → do **not** enable forwarding. Tell the operator the project is provisioned but forwarding stays off until the gate is met, and skip to Step 6.
|
|
223
|
-
- If **yes** → continue.
|
|
224
|
-
|
|
225
|
-
4. **Enable it in the backend** (reuse the `TOKEN` from Step 4):
|
|
226
|
-
|
|
227
|
-
```javascript
|
|
228
|
-
fetch('/api/feedback/v1/projects/PROJECT_UUID/observability/enable/', {
|
|
229
|
-
method: 'POST',
|
|
230
|
-
headers: { Authorization: 'Bearer TOKEN', 'Content-Type': 'application/json' },
|
|
231
|
-
body: JSON.stringify({ source_app: 'CLIENT_DO_APP_ID' }), // source_app optional; '' is fine
|
|
232
|
-
})
|
|
233
|
-
.then((r) => r.json())
|
|
234
|
-
.then((d) => JSON.stringify(d))
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
Capture `index_name` and `endpoint` from the response.
|
|
238
|
-
|
|
239
|
-
5. **Hand the client's DevOps this block** to add under the service that emits logs in their DO app spec, then `doctl apps update <app-id> --spec <file>` (or DO console → app → Settings → App Spec):
|
|
240
|
-
|
|
241
|
-
````markdown
|
|
242
|
-
### Mhosaic server-log forwarding — add to your DO app spec
|
|
243
|
-
|
|
244
|
-
```yaml
|
|
245
|
-
log_destinations:
|
|
246
|
-
- name: mhosaic-feedback
|
|
247
|
-
open_search:
|
|
248
|
-
endpoint: <endpoint from the response>
|
|
249
|
-
index_name: <index_name from the response>
|
|
250
|
-
basic_auth:
|
|
251
|
-
user: svc_log_forwarder
|
|
252
|
-
password: <get the svc_log_forwarder password from your Mhosaic operator>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
The `svc_log_forwarder` credential is **write-only** (it cannot read any logs) and shared across forwarding clients — get the password from the Mhosaic operator's secret store and don't commit it. Logs appear in the Observabilité tab within a few minutes of applying.
|
|
256
|
-
````
|
|
257
|
-
|
|
258
|
-
Present this as an addendum to the Step 5 handoff payload.
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
332
|
## Step 6 — Verify the project really exists
|
|
263
333
|
|
|
264
334
|
Drive Chrome to `https://software-factory-3tbbu.ondigitalocean.app/companies/<company-slug>` and screenshot. The operator should see the project listed. Then drive to `/settings/widget-keys` and confirm the key shows up there (masked — the plaintext is gone after this point).
|