instar 1.3.494 → 1.3.495

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.
@@ -809,6 +809,12 @@
809
809
  .mnd-btn { padding: 6px 14px; cursor: pointer; }
810
810
  .mnd-btn-danger { color: #f85149; }
811
811
  .mnd-dead-note { font-size: 13px; color: var(--text-dim); font-style: italic; }
812
+ .mnd-grants-head { font-size: 13px; color: var(--text-dim); }
813
+ .mnd-grants { margin: 0; padding-left: 18px; font-size: 13px; line-height: 1.7; }
814
+ .mnd-grant-details { font-size: 13px; }
815
+ .mnd-grant-summary { cursor: pointer; color: var(--text-dim); }
816
+ .mnd-grant-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 8px 0 4px; }
817
+ .mnd-grant-field { min-width: 130px; max-width: 100%; padding: 6px 8px; }
812
818
  .mnd-notice { min-height: 20px; font-size: 14px; color: #3fb950; }
813
819
  .mnd-issue-form { display: flex; flex-direction: column; gap: 14px; max-width: 640px; padding: 12px 0; }
814
820
  .mnd-issue-form label { display: flex; flex-direction: column; gap: 4px; font-size: 15px; color: var(--text-bright); }
@@ -20,6 +20,28 @@ const AUTHORITIES_TEMPLATE = JSON.stringify([
20
20
  { action: 'sign-code-review', bounds: { artifact: 'migration-port', mutual: true } },
21
21
  ], null, 2);
22
22
 
23
+ // Mirrors FLOOR_ACTIONS in src/permissions/RolePolicy.ts — the enumerated
24
+ // never-discretionary actions a user grant can lift. Kept in sync by the
25
+ // dashboard-mandatesTab test, which compares this list against the source enum.
26
+ const FLOOR_ACTIONS = [
27
+ 'money-movement',
28
+ 'prod-deploy',
29
+ 'credential-access',
30
+ 'destructive-data',
31
+ 'external-send',
32
+ 'grant-authority',
33
+ ];
34
+
35
+ // Mobile-first: the operator picks a duration, never types a timestamp.
36
+ // The submit handler clamps to the mandate's own expiry (a grant can never
37
+ // outlive the mandate that carries it — the server enforces the same rule).
38
+ const GRANT_DURATIONS = [
39
+ { label: '15 minutes', minutes: 15 },
40
+ { label: '1 hour', minutes: 60 },
41
+ { label: '4 hours', minutes: 240 },
42
+ { label: '24 hours', minutes: 1440 },
43
+ ];
44
+
23
45
  function esc(s) {
24
46
  return String(s ?? '').replace(/[&<>"']/g, (c) => ({
25
47
  '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
@@ -39,7 +61,57 @@ function fmtWhen(iso) {
39
61
  } catch { return String(iso); }
40
62
  }
41
63
 
42
- export function renderMandates(list) {
64
+ function userLabel(slackUserId, slackUsers) {
65
+ const u = (slackUsers || []).find((x) => x.slackUserId === slackUserId);
66
+ return u ? `${u.name} (${slackUserId})` : slackUserId;
67
+ }
68
+
69
+ /** The grants a mandate already carries, in operator language. */
70
+ export function renderGrants(m, slackUsers) {
71
+ const grants = Array.isArray(m.grants) ? m.grants : [];
72
+ if (grants.length === 0) return '';
73
+ const rows = grants.map((g) => {
74
+ const expired = Date.parse(g.expiresAt) < Date.now();
75
+ const badge = expired
76
+ ? '<span class="mnd-badge mnd-dead">expired</span>'
77
+ : '<span class="mnd-badge mnd-ok">active</span>';
78
+ return `<li>${badge} <strong>${esc(userLabel(g.grantedTo, slackUsers))}</strong> may <code>${esc(g.floorAction)}</code> until ${fmtWhen(g.expiresAt)} — authorized by ${esc(g.authorizedBy)}</li>`;
79
+ }).join('');
80
+ return `<div class="mnd-grants-head">User grants this mandate carries:</div><ul class="mnd-grants">${rows}</ul>`;
81
+ }
82
+
83
+ /**
84
+ * The add-grant form for an ACTIVE mandate. Mobile-first by design (the
85
+ * 2026-06-12 lesson, instar#1080): the operator PICKS a person and a duration —
86
+ * the only thing typed is the PIN. A free-text Slack-id input appears only
87
+ * when the user registry has nobody to offer.
88
+ */
89
+ export function renderGrantForm(m, slackUsers) {
90
+ const users = (slackUsers || []).filter((u) => u.slackUserId);
91
+ const granteeField = users.length > 0
92
+ ? `<select class="mnd-grant-field" data-grant-user="${esc(m.id)}">${
93
+ users.map((u) => `<option value="${esc(u.slackUserId)}">${esc(u.name)}${u.orgRole ? ` — ${esc(u.orgRole)}` : ''}</option>`).join('')
94
+ }</select>`
95
+ : `<input type="text" class="mnd-grant-field" data-grant-user="${esc(m.id)}" placeholder="Slack user id (e.g. U0…)" />`;
96
+ const actionField = `<select class="mnd-grant-field" data-grant-action="${esc(m.id)}">${
97
+ FLOOR_ACTIONS.map((a) => `<option value="${a}"${a === 'prod-deploy' ? ' selected' : ''}>${a}</option>`).join('')
98
+ }</select>`;
99
+ const durationField = `<select class="mnd-grant-field" data-grant-duration="${esc(m.id)}">${
100
+ GRANT_DURATIONS.map((d) => `<option value="${d.minutes}"${d.minutes === 60 ? ' selected' : ''}>${d.label}</option>`).join('')
101
+ }</select>`;
102
+ return `<details class="mnd-grant-details"><summary class="mnd-grant-summary">Grant a user a floor action (PIN required)</summary>
103
+ <div class="mnd-grant-row">
104
+ ${granteeField}
105
+ ${actionField}
106
+ ${durationField}
107
+ <input type="password" class="mnd-pin" data-grant-pin="${esc(m.id)}" placeholder="PIN" autocomplete="off" />
108
+ <button class="mnd-btn" data-grant="${esc(m.id)}">Grant</button>
109
+ </div>
110
+ <span class="mnd-hint">Lifts the picked person to ONE floor action for the picked window. Signed into this mandate by your PIN — revoking the mandate voids it; it can never outlive the mandate.</span>
111
+ </details>`;
112
+ }
113
+
114
+ export function renderMandates(list, slackUsers = []) {
43
115
  if (!Array.isArray(list) || list.length === 0) {
44
116
  return '<div class="mnd-empty">No mandates issued. The gate is deny-by-default: every delegated agent action refuses until you issue one.</div>';
45
117
  }
@@ -60,6 +132,7 @@ export function renderMandates(list) {
60
132
  <button class="mnd-btn mnd-btn-danger" data-revoke="${esc(m.id)}">Revoke</button>
61
133
  </div>`
62
134
  : `<div class="mnd-dead-note">${m.revoked ? `revoked ${fmtWhen(m.revoked.at)} — ${esc(m.revoked.reason)}` : `expired ${fmtWhen(m.expiresAt)}`}</div>`;
135
+ const grantUi = state === 'active' ? renderGrantForm(m, slackUsers) : '';
63
136
  return `<div class="mnd-card">
64
137
  <div class="mnd-card-head">
65
138
  <span class="mnd-scope">${esc(m.scope)}</span>
@@ -68,6 +141,8 @@ export function renderMandates(list) {
68
141
  </div>
69
142
  <div class="mnd-meta">id <code>${esc(m.id)}</code> · agents <code>${esc(shortFp(m.agents?.[0]))}</code> + <code>${esc(shortFp(m.agents?.[1]))}</code> · by ${esc(m.author)} · expires ${fmtWhen(m.expiresAt)}</div>
70
143
  <ul class="mnd-authorities">${authorities}</ul>
144
+ ${renderGrants(m, slackUsers)}
145
+ ${grantUi}
71
146
  ${revokeUi}
72
147
  </div>`;
73
148
  }).join('');
@@ -129,12 +204,18 @@ export function createController({ doc, els, fetchImpl }) {
129
204
 
130
205
  let refreshErrorShown = false;
131
206
  let autoOpenedIssueForm = false;
207
+ // Last-fetched state the grant submit handler needs: the mandate list (for
208
+ // the expiry clamp) — kept here, never re-derived from the DOM.
209
+ let lastMandates = [];
132
210
 
133
211
  async function refresh() {
134
212
  try {
135
- const [mand, audit] = await Promise.all([
213
+ const [mand, audit, users] = await Promise.all([
136
214
  fetchJson('/mandate'),
137
215
  fetchJson('/mandate/audit?limit=200'),
216
+ // Registered Slack users feed the grant form's person picker. A failure
217
+ // here must never take down the tab — the form degrades to a text input.
218
+ fetchJson('/permissions/users').catch(() => ({ status: 0, body: null })),
138
219
  ]);
139
220
  if (mand.status === 503) {
140
221
  els.list.innerHTML = '<div class="mnd-empty">Mandate engine unavailable on this server (older version or init failure).</div>';
@@ -143,7 +224,9 @@ export function createController({ doc, els, fetchImpl }) {
143
224
  return;
144
225
  }
145
226
  const mandates = mand.body?.mandates;
146
- els.list.innerHTML = renderMandates(mandates);
227
+ lastMandates = Array.isArray(mandates) ? mandates : [];
228
+ const slackUsers = users.status === 200 && Array.isArray(users.body?.users) ? users.body.users : [];
229
+ els.list.innerHTML = renderMandates(mandates, slackUsers);
147
230
  els.audit.innerHTML = renderAudit(audit.body);
148
231
  if (els.stamp) els.stamp.textContent = 'updated ' + new Date().toLocaleTimeString();
149
232
  // Nothing issued yet → the issue form IS the page's call to action;
@@ -155,6 +238,7 @@ export function createController({ doc, els, fetchImpl }) {
155
238
  // A persistent refresh error from a server restart-gap heals itself.
156
239
  if (refreshErrorShown) { clearNote(); refreshErrorShown = false; }
157
240
  wireRevokeButtons();
241
+ wireGrantButtons();
158
242
  } catch (e) {
159
243
  refreshErrorShown = true;
160
244
  note('refresh failed: ' + (e?.message ?? e) + ' — retrying automatically.', 'error');
@@ -184,6 +268,66 @@ export function createController({ doc, els, fetchImpl }) {
184
268
  });
185
269
  }
186
270
 
271
+ function wireGrantButtons() {
272
+ els.list.querySelectorAll('[data-grant]').forEach((btn) => {
273
+ btn.onclick = async () => {
274
+ const id = btn.getAttribute('data-grant');
275
+ const userEl = els.list.querySelector(`[data-grant-user="${id}"]`);
276
+ const actionEl = els.list.querySelector(`[data-grant-action="${id}"]`);
277
+ const durEl = els.list.querySelector(`[data-grant-duration="${id}"]`);
278
+ const pinEl = els.list.querySelector(`[data-grant-pin="${id}"]`);
279
+ const grantedTo = (userEl?.value ?? '').trim();
280
+ const floorAction = actionEl?.value ?? 'prod-deploy';
281
+ const pin = pinEl?.value ?? '';
282
+ const problems = [];
283
+ if (!grantedTo) problems.push('• Pick (or type) who the grant is for.');
284
+ if (!pin) problems.push('• Type your dashboard PIN — granting a floor action is a human action; agent credentials are refused.');
285
+ if (problems.length > 0) {
286
+ note('Not granted — fix the following first:\n' + problems.join('\n'), 'error');
287
+ return;
288
+ }
289
+ // A grant can never outlive its mandate — clamp client-side so the
290
+ // operator's pick always succeeds (the server enforces the same rule
291
+ // by rejection; rejection is a worse experience than a shorter window).
292
+ const minutes = Number(durEl?.value ?? 60) || 60;
293
+ const mandate = lastMandates.find((m) => m.id === id);
294
+ const mandateExpiryMs = mandate ? Date.parse(mandate.expiresAt) : NaN;
295
+ let expiryMs = Date.now() + minutes * 60_000;
296
+ let clamped = false;
297
+ if (!isNaN(mandateExpiryMs) && expiryMs > mandateExpiryMs) { expiryMs = mandateExpiryMs; clamped = true; }
298
+ btn.disabled = true;
299
+ try {
300
+ const { status, body } = await fetchJson(`/mandate/${encodeURIComponent(id)}/grants`, {
301
+ method: 'POST',
302
+ headers: { 'Content-Type': 'application/json' },
303
+ body: JSON.stringify({
304
+ pin,
305
+ grants: [{
306
+ floorAction,
307
+ grantedTo,
308
+ authorizedBy: 'operator (dashboard PIN)',
309
+ expiresAt: new Date(expiryMs).toISOString(),
310
+ }],
311
+ }),
312
+ });
313
+ if (status === 201) {
314
+ note(`✓ Grant signed — ${grantedTo} may ${floorAction} until ${fmtWhen(new Date(expiryMs).toISOString())}${clamped ? ' (shortened to the mandate’s own expiry — a grant can never outlive its mandate)' : ''}.`, 'success');
315
+ await refresh();
316
+ } else {
317
+ note(`Not granted — the server refused: ${body?.error ?? `HTTP ${status}`}`, 'error');
318
+ }
319
+ } catch (e) {
320
+ // A network failure must neither strand the typed PIN (cleared in
321
+ // finally) nor fail silently.
322
+ note(`Not granted — request failed: ${e?.message ?? e}. Nothing was signed; try again.`, 'error');
323
+ } finally {
324
+ if (pinEl) pinEl.value = ''; // never retain the PIN — on ANY path
325
+ btn.disabled = false;
326
+ }
327
+ };
328
+ });
329
+ }
330
+
187
331
  // Validate EVERYTHING client-side and report every problem at once —
188
332
  // never POST a request we know the server will refuse, and never let the
189
333
  // operator discover a missing field one transient error at a time.
@@ -1 +1 @@
1
- {"version":3,"file":"PostUpdateMigrator.d.ts","sourceRoot":"","sources":["../../src/core/PostUpdateMigrator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PostUpdateMigrator.d.ts","sourceRoot":"","sources":["../../src/core/PostUpdateMigrator.ts"],"names":[],"mappings":"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"}
@@ -5027,6 +5027,24 @@ Create worktrees for collaborator repos with \`instar worktree create <branch>\`
5027
5027
  patched = true;
5028
5028
  result.upgraded.push('CLAUDE.md: added Coordination Mandate awareness section');
5029
5029
  }
5030
+ // Phone-first floor grants (Mobile-Complete Operator Actions, instar#1080)
5031
+ // — agents that already carry the Coordination Mandate section must learn
5032
+ // to point operators at the Mandates-tab grant form, never at a terminal
5033
+ // command. Content-sniffed on the bullet's distinctive lead; inserted
5034
+ // inside the existing section when its anchor line is intact, appended
5035
+ // otherwise so a hand-edited section still gains the guidance.
5036
+ if (content.includes('/mandate/evaluate') && !content.includes('User floor-action grants are phone-first')) {
5037
+ const grantBullet = `- **User floor-action grants are phone-first.** When the operator needs to grant a USER a floor action (e.g. "let Mia prod-deploy for an hour"), the Mandates tab carries a grant form on every active mandate: pick the person (from the registered-user list), pick the action and duration, type the PIN, tap Grant. Send them the dashboard link — NEVER a terminal command or a hand-built API call (Mobile-Complete Operator Actions). The grant is signed into the mandate, clamped to the mandate's expiry, and voided by revoking the mandate.`;
5038
+ const grantAnchor = 'point them at the dashboard **Mandates tab** (issue/revoke forms + the decision audit live there).';
5039
+ if (content.includes(grantAnchor)) {
5040
+ content = content.replace(grantAnchor, grantAnchor + '\n' + grantBullet);
5041
+ }
5042
+ else {
5043
+ content += '\n' + grantBullet + '\n';
5044
+ }
5045
+ patched = true;
5046
+ result.upgraded.push('CLAUDE.md: added phone-first floor-grant guidance to the Coordination Mandate section');
5047
+ }
5030
5048
  // ReviewExchange protocol (coordination-mandate spec §7 G2.3) — Agent
5031
5049
  // Awareness backfill. Content-sniffed on the distinctive route prefix.
5032
5050
  if (!content.includes('/review-exchange')) {