@markjaquith/agency 2.30.1 → 2.31.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/README.md CHANGED
@@ -37,6 +37,12 @@ For development, run `bun link` from this repository.
37
37
  Entity IDs come from directory names. Structured metadata lives in YAML 1.2
38
38
  frontmatter; prose below it supplies human and agent context.
39
39
 
40
+ New epic, task, and phase documents use the same core prose sections:
41
+ `Outcome` states the intended result, `Plan` describes the current approach, and
42
+ `Important Decisions` preserves consequential choices and their rationale. These
43
+ sections are creation defaults rather than validation requirements, so existing
44
+ and customized documents remain valid.
45
+
40
46
  ## Workbase Layout
41
47
 
42
48
  ```text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "2.30.1",
3
+ "version": "2.31.0",
4
4
  "description": "Manage agentic work across repositories with durable workbases",
5
5
  "keywords": [
6
6
  "agents",
@@ -34,6 +34,9 @@ describe("EpicService", () => {
34
34
 
35
35
  expect(created.content).toContain("ticketUrl:")
36
36
  expect(created.content).toContain("tasks: []")
37
+ expect(created.content).toContain(
38
+ "# Workspace Orchestration\n\n## Outcome\n\nDescribe the epic outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions\n\nRecord consequential decisions and their rationale.",
39
+ )
37
40
 
38
41
  const records = await runTestEffect(
39
42
  EpicService.pipe(Effect.flatMap((service) => service.list(root))),
@@ -10,6 +10,7 @@ import {
10
10
  } from "../workbase/schemas"
11
11
  import {
12
12
  formatMarkdownDocument,
13
+ formatWorkDocumentBody,
13
14
  parseFrontmatter,
14
15
  } from "../workbase/frontmatter"
15
16
  import { documentRevision } from "../workbase/document-revision"
@@ -95,7 +96,7 @@ export class EpicService extends Effect.Service<EpicService>()("EpicService", {
95
96
  .join(" ")
96
97
  const content = formatMarkdownDocument(
97
98
  data,
98
- `# ${title}\n\nDescribe the epic outcome.`,
99
+ formatWorkDocumentBody(title, "epic"),
99
100
  )
100
101
  yield* fs.writeFile(path, content)
101
102
  return {
@@ -14,6 +14,7 @@ import {
14
14
  } from "../workbase/schemas"
15
15
  import {
16
16
  formatMarkdownDocument,
17
+ formatWorkDocumentBody,
17
18
  parseFrontmatter,
18
19
  } from "../workbase/frontmatter"
19
20
  import { canTransitionStatus } from "../readiness"
@@ -196,7 +197,7 @@ export class PhaseService extends Effect.Service<PhaseService>()(
196
197
  .join(" ")
197
198
  const content = formatMarkdownDocument(
198
199
  data,
199
- `# ${title}\n\nDescribe the phase outcome.`,
200
+ formatWorkDocumentBody(title, "phase"),
200
201
  )
201
202
 
202
203
  if (!isMultiPhase) {
@@ -246,7 +247,7 @@ export class PhaseService extends Effect.Service<PhaseService>()(
246
247
  const firstPhasePath = join(firstDirectory, "PHASE.md")
247
248
  const firstContent = formatMarkdownDocument(
248
249
  firstData,
249
- `# ${firstTitle}\n\nDescribe the phase outcome.`,
250
+ formatWorkDocumentBody(firstTitle, "phase"),
250
251
  )
251
252
  const steps: TransactionStep[] = []
252
253
  if (yield* fs.isDirectory(oldCodePath)) {
@@ -220,7 +220,10 @@ process.exit(${exitCode})
220
220
  const original = await Bun.file(taskPath).text()
221
221
  await Bun.write(
222
222
  taskPath,
223
- original.replace("# Example\n\nDescribe the task outcome.", body),
223
+ original.replace(
224
+ "# Example\n\n## Outcome\n\nDescribe the task outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions\n\nRecord consequential decisions and their rationale.",
225
+ body,
226
+ ),
224
227
  )
225
228
  const url = "https://github.com/example/agency/pull/42"
226
229
  await writeFakeGh({ stdout: `${url}\n` })
@@ -33,7 +33,7 @@ describe("task and phase services", () => {
33
33
  ),
34
34
  ),
35
35
  )
36
- await runTestEffect(
36
+ const createdTask = await runTestEffect(
37
37
  TaskService.pipe(
38
38
  Effect.flatMap((service) =>
39
39
  service.create(
@@ -51,6 +51,9 @@ describe("task and phase services", () => {
51
51
  ),
52
52
  ),
53
53
  )
54
+ expect(createdTask.content).toContain(
55
+ "# Task One\n\n## Outcome\n\nDescribe the task outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions\n\nRecord consequential decisions and their rationale.",
56
+ )
54
57
 
55
58
  const epic = await runTestEffect(
56
59
  EpicService.pipe(
@@ -159,6 +162,14 @@ describe("task and phase services", () => {
159
162
  ),
160
163
  )
161
164
  expect(phases.map((phase) => phase.id)).toEqual(["first", "second"])
165
+ const firstPhase = await runTestEffect(
166
+ PhaseService.pipe(
167
+ Effect.flatMap((service) => service.show("multi", "first", root)),
168
+ ),
169
+ )
170
+ expect(firstPhase.content).toContain(
171
+ "# First\n\n## Outcome\n\nDescribe the phase outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions\n\nRecord consequential decisions and their rationale.",
172
+ )
162
173
  })
163
174
 
164
175
  test("converts a single-phase task when the existing phase ID is provided", async () => {
@@ -243,7 +254,9 @@ describe("task and phase services", () => {
243
254
  { id: "extra", dependsOn: ["implementation"] },
244
255
  ],
245
256
  })
246
- expect(task.content).toContain("Describe the task outcome.")
257
+ expect(task.content).toContain(
258
+ "## Outcome\n\nDescribe the task outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions",
259
+ )
247
260
 
248
261
  const firstPhase = await runTestEffect(
249
262
  PhaseService.pipe(
@@ -13,6 +13,7 @@ import {
13
13
  } from "../workbase/schemas"
14
14
  import {
15
15
  formatMarkdownDocument,
16
+ formatWorkDocumentBody,
16
17
  parseFrontmatter,
17
18
  } from "../workbase/frontmatter"
18
19
  import { canTransitionStatus } from "../readiness"
@@ -163,7 +164,7 @@ export class TaskService extends Effect.Service<TaskService>()("TaskService", {
163
164
  .join(" ")
164
165
  const content = formatMarkdownDocument(
165
166
  data,
166
- `# ${title}\n\nDescribe the task outcome.`,
167
+ formatWorkDocumentBody(title, "task"),
167
168
  )
168
169
  const writes: {
169
170
  path: string
@@ -81,3 +81,20 @@ export const parseFrontmatter = (content: string, path: string) =>
81
81
 
82
82
  export const formatMarkdownDocument = (data: object, body: string) =>
83
83
  `---\n${stringify(data, { lineWidth: 0 }).trimEnd()}\n---\n\n${body.trim()}\n`
84
+
85
+ export const formatWorkDocumentBody = (
86
+ title: string,
87
+ kind: "epic" | "task" | "phase",
88
+ ) => `# ${title}
89
+
90
+ ## Outcome
91
+
92
+ Describe the ${kind} outcome.
93
+
94
+ ## Plan
95
+
96
+ Describe the current approach.
97
+
98
+ ## Important Decisions
99
+
100
+ Record consequential decisions and their rationale.`