Public: anyone can read this; the crew can change it.

Errors on the crew page are §5's field error, not .error-note #5091

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/5069-errors-on-the-crew-page-are-5-s-field-er → main 1 commit fork

  • Merged. scout/5069-errors-on-the-crew-page-are-5-s-field-er was merged into main as f9b53a6.

Review

Sign in to review this change.
2 files changed +43 −16
src/web/pages/crew.ts +25 −15
@@ -100,27 +100,37 @@ function shell(ctx: Ctx, title: string, body: Html, status = 200): Response {
100100 export function crewReadiness(ctx: Ctx): Html {
101101 const crew = crewFor(ctx.app);
102102 if (crew === undefined)
103 return html`<p class="error-note mb">
104 ${icon("alert")} No crew runner is up on this forge, so assigned issues
105 will wait. Start the forge with apps on (the default) and Claude Code on
106 <span class="mono">PATH</span> or at
107 <span class="mono">FORGE_CLAUDE</span>.
108 </p>`;
103 return card(
104 html`<p>
105 ${icon("alert")} No crew runner is up on this forge, so assigned issues
106 will wait. Start the forge with apps on (the default) and Claude Code
107 on <span class="mono">PATH</span> or at
108 <span class="mono">FORGE_CLAUDE</span>.
109 </p>`,
110 { tone: "danger", extra: "mb" },
111 );
109112 if (!crew.ready)
110 return html`<p class="error-note mb">
111 ${icon("alert")} The crew cannot run: ${crew.detail}. Agents can be
112 created, but nothing picks up their work until this is fixed.
113 </p>`;
113 return card(
114 html`<p>
115 ${icon("alert")} The crew cannot run: ${crew.detail}. Agents can be
116 created, but nothing picks up their work until this is fixed.
117 </p>`,
118 { tone: "danger", extra: "mb" },
119 );
114120 return html`<p class="muted small mb">
115121 ${icon("check")} ${crew.detail} is ready to run the crew.
116122 </p>
117123 ${when(
118124 !crew.sandbox.enforced,
119 () => html`<p class="error-note mb" id="crew-unsandboxed">
120 ${icon("alert")} Unsandboxed: agents' shells run unrestricted here
121 (${crew.sandbox.reason}). They can read this forge's data and reach any
122 host.
123 </p>`,
125 () =>
126 card(
127 html`<p>
128 ${icon("alert")} Unsandboxed: agents' shells run unrestricted here
129 (${crew.sandbox.reason}). They can read this forge's data and reach
130 any host.
131 </p>`,
132 { tone: "danger", extra: "mb", id: "crew-unsandboxed" },
133 ),
124134 )}`;
125135 }
126136
test/web-apps.test.ts +18 −1
@@ -735,7 +735,13 @@ describe("apps, runs and the crew on the web", () => {
735735 form: { username: "bot", budgetUsd: "-4" },
736736 });
737737 expect(bad.status).toBe(422);
738 expect(words(await bad.text())).toContain("A budget is a number");
738 const badPage = words(await bad.text());
739 expect(badPage).toContain("A budget is a number");
740 // The error sits beside its field (§5), not in an .error-note.
741 expect(badPage).toMatch(
742 /<div class="field" data-invalid>\s*<label for="f-budgetUsd">.*?<span class="error" id="f-budgetUsd-err">A budget is a number/,
743 );
744 expect(badPage).not.toContain("error-note");
739745 expect(getAgentByName(app.db, "bot")).toBeUndefined();
740746
741747 const made = await web(alice, "/settings/crew", {
@@ -764,6 +770,17 @@ describe("apps, runs and the crew on the web", () => {
764770 expect(updated.status).toBe(303);
765771 expect(getAgentByName(app.db, "bot")?.model).toBe("sonnet");
766772 expect(getAgentByName(app.db, "bot")?.budgetUsd).toBe(3);
773 // A refused edit shows the error beside its field.
774 const refused = await web(alice, "/settings/crew/bot", {
775 form: { model: "sonnet", brief: "Be brief.", budgetUsd: "-4" },
776 });
777 expect(refused.status).toBe(422);
778 const refusedPage = words(await refused.text());
779 expect(refusedPage).toMatch(
780 /<div class="field" data-invalid>\s*<label for="f-budgetUsd">.*?<span class="error" id="f-budgetUsd-err">A budget is a number/,
781 );
782 expect(refusedPage).not.toContain("error-note");
783 expect(getAgentByName(app.db, "bot")?.budgetUsd).toBe(3);
767784 // A non-admin cannot touch any of it, whatever the form says.
768785 const forged = await web(bob, "/settings/crew/bot", {
769786 form: { model: "opus" },

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs