Public: anyone can read this; the crew can change it.
"sign in to act on your asks" lives in one module, read by bulk decide, decide and proposals #5631
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/5614-sign-in-to-act-on-your-asks-lives-in-one → main 1 commit fork
5 files
changed +37
−4
src/actions/asks_bulk.ts +2 −1
| @@ -23,6 +23,7 @@ import { | ||
| 23 | 23 | type FleetError, |
| 24 | 24 | type Plan, |
| 25 | 25 | } from "./fleet.ts"; |
| 26 | import { SIGN_IN_TO_ACT } from "./asks_words.ts"; | |
| 26 | 27 | |
| 27 | 28 | /** |
| 28 | 29 | * What a bulk action on asks may be: the fleet verb each one is. Decline is |
| @@ -59,7 +60,7 @@ function resolve( | ||
| 59 | 60 | if (!actor) |
| 60 | 61 | return err({ |
| 61 | 62 | code: "unauthorized", |
| 62 | message: "sign in to act on your asks", | |
| 63 | message: SIGN_IN_TO_ACT, | |
| 63 | 64 | }); |
| 64 | 65 | const verb = |
| 65 | 66 | typeof action === "string" && Object.hasOwn(ASK_ACTIONS, action) |
src/actions/asks_decide.ts +3 −2
| @@ -15,6 +15,7 @@ import { guardedHand, letDeciderHandle, undoHandled } from "../crew/handover.ts" | ||
| 15 | 15 | import { err, ok, type Result } from "../kernel/result.ts"; |
| 16 | 16 | import { askTargetsFor } from "../web/studio/asks.ts"; |
| 17 | 17 | import type { FleetError } from "./fleet.ts"; |
| 18 | import { SIGN_IN_TO_ACT } from "./asks_words.ts"; | |
| 18 | 19 | |
| 19 | 20 | export { HAND_COOLDOWN_MS } from "../crew/handover.ts"; |
| 20 | 21 | |
| @@ -57,7 +58,7 @@ export async function decideAsk( | ||
| 57 | 58 | item: unknown, |
| 58 | 59 | now = Date.now(), |
| 59 | 60 | ): Promise<Result<HandAnswer, FleetError>> { |
| 60 | if (!actor) return fail("unauthorized", "sign in to act on your asks"); | |
| 61 | if (!actor) return fail("unauthorized", SIGN_IN_TO_ACT); | |
| 61 | 62 | if (typeof item !== "string" || !ASK_ID.test(item)) |
| 62 | 63 | return fail("bad_action", "only a hand-over can be handed to the decider"); |
| 63 | 64 | if (app.config.decider === undefined) |
| @@ -86,7 +87,7 @@ export function undoDecideAsk( | ||
| 86 | 87 | actor: User | undefined, |
| 87 | 88 | decisionId: unknown, |
| 88 | 89 | ): Result<HandAnswer, FleetError> { |
| 89 | if (!actor) return fail("unauthorized", "sign in to act on your asks"); | |
| 90 | if (!actor) return fail("unauthorized", SIGN_IN_TO_ACT); | |
| 90 | 91 | if (typeof decisionId !== "string" || decisionId === "") |
| 91 | 92 | return fail("not_found", "no such decision"); |
| 92 | 93 | const undone = undoHandled(app, actor, decisionId); |
src/actions/asks_words.ts added +8
| @@ -0,0 +1,8 @@ | ||
| 1 | /** | |
| 2 | * What acting on asks says, said from one place (#5614): bulk decide, | |
| 3 | * decide and proposals read it here, so the web, the API, MCP and the | |
| 4 | * terminal word it the same. | |
| 5 | */ | |
| 6 | ||
| 7 | /** Acting on an ask while signed out. */ | |
| 8 | export const SIGN_IN_TO_ACT = "sign in to act on your asks"; |
src/actions/proposals.ts +2 −1
| @@ -22,6 +22,7 @@ import { NOT_A_MEMBER } from "../core/roles.ts"; | ||
| 22 | 22 | import { requireAbility } from "./roles.ts"; |
| 23 | 23 | import { assignIssue, addComment } from "./issues.ts"; |
| 24 | 24 | import { openPlan, parsePlan, type Planned } from "./plan.ts"; |
| 25 | import { SIGN_IN_TO_ACT } from "./asks_words.ts"; | |
| 25 | 26 | |
| 26 | 27 | export interface ProposalError { |
| 27 | 28 | readonly code: "unauthorized" | "not_found" | "forbidden" | "invalid" | "conflict"; |
| @@ -45,7 +46,7 @@ function mine( | ||
| 45 | 46 | id: unknown, |
| 46 | 47 | states: readonly ProposalState[], |
| 47 | 48 | ): Result<{ proposal: Proposal; repo: Repo; actor: User }, ProposalError> { |
| 48 | if (!actor) return fail("unauthorized", "sign in to act on your asks"); | |
| 49 | if (!actor) return fail("unauthorized", SIGN_IN_TO_ACT); | |
| 49 | 50 | const proposal = typeof id === "string" ? getProposal(app.db, id) : undefined; |
| 50 | 51 | const repo = proposal ? getRepoById(app.db, proposal.repoId) : undefined; |
| 51 | 52 | if (!proposal || !repo || !canRead(app.db, repo, actor)) |
test/asks-words.test.ts added +22
| @@ -0,0 +1,22 @@ | ||
| 1 | /** "sign in to act on your asks" is typed once, in actions/asks_words.ts (#5614). */ | |
| 2 | import { describe, expect, test } from "bun:test"; | |
| 3 | import { readFileSync } from "node:fs"; | |
| 4 | import { join } from "node:path"; | |
| 5 | import { SIGN_IN_TO_ACT } from "../src/actions/asks_words.ts"; | |
| 6 | ||
| 7 | const READERS = ["asks_bulk.ts", "asks_decide.ts", "proposals.ts"]; | |
| 8 | const text = (file: string) => readFileSync(join(import.meta.dir, "../src/actions", file), "utf8"); | |
| 9 | ||
| 10 | describe("asks words", () => { | |
| 11 | test("the sentence reads as it always has", () => { | |
| 12 | expect(SIGN_IN_TO_ACT).toBe("sign in to act on your asks"); | |
| 13 | }); | |
| 14 | ||
| 15 | for (const file of READERS) { | |
| 16 | test(`${file} imports the sentence and types no copy of it`, () => { | |
| 17 | const source = text(file); | |
| 18 | expect(source).not.toContain(SIGN_IN_TO_ACT); | |
| 19 | expect(source).toContain('import { SIGN_IN_TO_ACT } from "./asks_words.ts";'); | |
| 20 | }); | |
| 21 | } | |
| 22 | }); |
new above
offline