@markjaquith/agency 2.48.1 → 2.49.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 +18 -8
- package/cli.ts +9 -0
- package/package.json +1 -1
- package/src/cli-parser.test.ts +58 -0
- package/src/cli-parser.ts +67 -6
- package/src/cli.test.ts +71 -0
- package/src/commands/claim.ts +26 -2
- package/src/commands/phase.ts +23 -2
- package/src/commands/task.ts +22 -2
- package/src/services/ClaimService.test.ts +59 -0
- package/src/services/ClaimService.ts +34 -1
- package/src/services/GraphMutationService.ts +16 -0
- package/src/services/IntegrationService.test.ts +4 -1
- package/src/services/PhaseService.ts +50 -3
- package/src/services/PullRequestService.test.ts +20 -0
- package/src/services/PullRequestService.ts +24 -3
- package/src/services/SyncService.test.ts +66 -0
- package/src/services/SyncService.ts +13 -0
- package/src/services/TaskPhaseService.test.ts +182 -0
- package/src/services/TaskService.ts +47 -3
- package/src/services/WorkbaseService.test.ts +58 -0
- package/src/services/WorkbaseService.ts +20 -0
- package/src/workbase/AGENTS.md +7 -2
- package/src/workbase/completion.ts +28 -0
- package/src/workbase/schemas.ts +9 -0
|
@@ -746,6 +746,24 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
746
746
|
}
|
|
747
747
|
}
|
|
748
748
|
|
|
749
|
+
const validateCompletion = (
|
|
750
|
+
record: DocumentRecord<TaskData | PhaseData>,
|
|
751
|
+
) => {
|
|
752
|
+
if (!("repo" in record.data) || !record.data.completion) return
|
|
753
|
+
if (record.data.status !== "done") {
|
|
754
|
+
issue(record.path, "Non-PR completion requires status 'done'")
|
|
755
|
+
}
|
|
756
|
+
if (record.data.pr !== null) {
|
|
757
|
+
issue(
|
|
758
|
+
record.path,
|
|
759
|
+
"Non-PR completion cannot have a recorded pull request",
|
|
760
|
+
)
|
|
761
|
+
}
|
|
762
|
+
if (record.data.claim?.state === "active") {
|
|
763
|
+
issue(record.path, "Completed work cannot have an active claim")
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
749
767
|
for (const epic of epics.values()) {
|
|
750
768
|
validateRepositories(epic)
|
|
751
769
|
const ids = new Set(epic.data.tasks.map((task) => task.id))
|
|
@@ -776,6 +794,7 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
776
794
|
for (const task of tasks.values()) {
|
|
777
795
|
validateRepositories(task)
|
|
778
796
|
validateBranchOwnership(task)
|
|
797
|
+
validateCompletion(task)
|
|
779
798
|
if (task.data.epic) {
|
|
780
799
|
const parent = epics.get(task.data.epic)
|
|
781
800
|
if (!parent) {
|
|
@@ -828,6 +847,7 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
828
847
|
for (const phase of phases.values()) {
|
|
829
848
|
validateRepositories(phase)
|
|
830
849
|
validateBranchOwnership(phase)
|
|
850
|
+
validateCompletion(phase)
|
|
831
851
|
}
|
|
832
852
|
|
|
833
853
|
issues.sort((a, b) =>
|
package/src/workbase/AGENTS.md
CHANGED
|
@@ -43,7 +43,8 @@ reason to edit `agency.json` or `repos/` by hand.
|
|
|
43
43
|
Require explicit user intent before initializing a workbase; changing repository
|
|
44
44
|
aliases or applying repository setup or workbase sync changes; launching another
|
|
45
45
|
agent from an active agent session; creating a pull request; archiving, restoring,
|
|
46
|
-
dropping, or
|
|
46
|
+
dropping, reopening, or completing work without a pull request; or using `--force`
|
|
47
|
+
to override readiness.
|
|
47
48
|
|
|
48
49
|
## Safety
|
|
49
50
|
|
|
@@ -80,7 +81,9 @@ release the claim with the current document revision.
|
|
|
80
81
|
An execution unit remains `working` after implementation is committed and while
|
|
81
82
|
its pull request is open. It becomes `done` only after its authoritative pull
|
|
82
83
|
request is merged and Agency reconciles that state. Do not mark committed or
|
|
83
|
-
review-ready work `done` manually.
|
|
84
|
+
review-ready work `done` manually. A genuine investigation, operational action,
|
|
85
|
+
or no-change result may complete without a pull request only with explicit user
|
|
86
|
+
intent, `--no-pull-request`, and a durable outcome summary.
|
|
84
87
|
|
|
85
88
|
At each closeout trigger (creating or updating a PR, marking it ready, completing
|
|
86
89
|
a refinement loop, or pausing or handing off completed implementation work):
|
|
@@ -90,6 +93,8 @@ a refinement loop, or pausing or handing off completed implementation work):
|
|
|
90
93
|
keep the execution unit `working` through review and merge.
|
|
91
94
|
- After merge, run `agency sync --apply` to reconcile the execution unit to
|
|
92
95
|
`done`.
|
|
96
|
+
- For an approved non-PR outcome, finish an active claim or update unclaimed
|
|
97
|
+
status with `--no-pull-request --summary <text>` and optional supporting URL.
|
|
93
98
|
- Refresh durable delivery context in `TASK.md` or `PHASE.md`, including recorded
|
|
94
99
|
PR state, current head, diff summary, and verification results after later
|
|
95
100
|
pushes when those details are maintained there.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CompletionRecord } from "./schemas"
|
|
2
|
+
|
|
3
|
+
export interface NonPrCompletionInput {
|
|
4
|
+
readonly summary: string
|
|
5
|
+
readonly evidenceUrl?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const buildNonPrCompletion = (
|
|
9
|
+
input: NonPrCompletionInput,
|
|
10
|
+
now: Date,
|
|
11
|
+
): { readonly value: CompletionRecord } | { readonly error: string } => {
|
|
12
|
+
const summary = input.summary.trim()
|
|
13
|
+
if (!summary) return { error: "Non-PR completion summary must not be empty" }
|
|
14
|
+
|
|
15
|
+
const evidenceUrl = input.evidenceUrl?.trim()
|
|
16
|
+
if (evidenceUrl && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(evidenceUrl)) {
|
|
17
|
+
return { error: `Invalid completion evidence URL: ${evidenceUrl}` }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
value: {
|
|
22
|
+
mode: "non-pr",
|
|
23
|
+
completedAt: now.toISOString(),
|
|
24
|
+
summary,
|
|
25
|
+
...(evidenceUrl ? { evidenceUrl } : {}),
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/workbase/schemas.ts
CHANGED
|
@@ -75,6 +75,13 @@ export const PullRequestRecord = Schema.Struct({
|
|
|
75
75
|
mergeable: Schema.optional(Schema.NullOr(Schema.Boolean)),
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
+
export const CompletionRecord = Schema.Struct({
|
|
79
|
+
mode: Schema.Literal("non-pr"),
|
|
80
|
+
completedAt: IsoTimestamp,
|
|
81
|
+
summary: NonEmptyString,
|
|
82
|
+
evidenceUrl: Schema.optional(Url),
|
|
83
|
+
})
|
|
84
|
+
|
|
78
85
|
const DeliveryProvider = Schema.Struct({
|
|
79
86
|
provider: EntityId,
|
|
80
87
|
remote: Schema.optional(NonEmptyString),
|
|
@@ -141,6 +148,7 @@ const ExecutionUnit = {
|
|
|
141
148
|
pr: Schema.NullOr(Schema.Union(GitHubPullRequestUrl, PullRequestRecord)),
|
|
142
149
|
status: Schema.optionalWith(WorkStatus, { default: () => "open" as const }),
|
|
143
150
|
claim: Schema.optional(ClaimRecord),
|
|
151
|
+
completion: Schema.optional(CompletionRecord),
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
export const EpicFrontmatter = Schema.Struct({
|
|
@@ -187,6 +195,7 @@ export type RepositoryDeclaration = Schema.Schema.Type<
|
|
|
187
195
|
export type WorkStatus = Schema.Schema.Type<typeof WorkStatus>
|
|
188
196
|
export type ClaimRecord = Schema.Schema.Type<typeof ClaimRecord>
|
|
189
197
|
export type PullRequestRecord = Schema.Schema.Type<typeof PullRequestRecord>
|
|
198
|
+
export type CompletionRecord = Schema.Schema.Type<typeof CompletionRecord>
|
|
190
199
|
export type EpicFrontmatter = Schema.Schema.Type<typeof EpicFrontmatter>
|
|
191
200
|
export type TaskFrontmatter = Schema.Schema.Type<typeof TaskFrontmatter>
|
|
192
201
|
export type PhaseFrontmatter = Schema.Schema.Type<typeof PhaseFrontmatter>
|