@clemrig/factory 0.18.1 → 0.19.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 +15 -19
- package/dist/app/factory.d.ts +2 -0
- package/dist/app/factory.js +5 -1
- package/dist/app/factory.js.map +1 -1
- package/dist/app/review.d.ts +16 -0
- package/dist/app/review.js +87 -0
- package/dist/app/review.js.map +1 -0
- package/dist/app/workflows/implement.js +3 -7
- package/dist/app/workflows/implement.js.map +1 -1
- package/dist/app/workflows/stages/deliver.d.ts +5 -4
- package/dist/app/workflows/stages/deliver.js +47 -53
- package/dist/app/workflows/stages/deliver.js.map +1 -1
- package/dist/app/workflows/stages/prepare.js +11 -7
- package/dist/app/workflows/stages/prepare.js.map +1 -1
- package/dist/app/workflows/stages/resume.js +0 -4
- package/dist/app/workflows/stages/resume.js.map +1 -1
- package/dist/app/workflows/stages/review.d.ts +7 -5
- package/dist/app/workflows/stages/review.js +75 -24
- package/dist/app/workflows/stages/review.js.map +1 -1
- package/dist/app/workflows/stages/run.d.ts +1 -2
- package/dist/app/workflows/tidy.js +2 -3
- package/dist/app/workflows/tidy.js.map +1 -1
- package/dist/cli/ui.d.ts +4 -2
- package/dist/cli/ui.js +17 -5
- package/dist/cli/ui.js.map +1 -1
- package/dist/domain/board.d.ts +2 -0
- package/dist/domain/board.js +4 -1
- package/dist/domain/board.js.map +1 -1
- package/dist/domain/description.d.ts +57 -5
- package/dist/domain/description.js +133 -12
- package/dist/domain/description.js.map +1 -1
- package/dist/domain/diff.d.ts +8 -0
- package/dist/domain/diff.js +90 -0
- package/dist/domain/diff.js.map +1 -0
- package/dist/domain/pull-request.d.ts +9 -21
- package/dist/domain/pull-request.js +44 -229
- package/dist/domain/pull-request.js.map +1 -1
- package/dist/domain/review-page.d.ts +171 -0
- package/dist/domain/review-page.js +200 -0
- package/dist/domain/review-page.js.map +1 -0
- package/dist/domain/review.d.ts +85 -5
- package/dist/domain/review.js +109 -18
- package/dist/domain/review.js.map +1 -1
- package/dist/domain/spec.d.ts +1 -1
- package/dist/domain/spec.js.map +1 -1
- package/dist/system/environment.d.ts +6 -0
- package/dist/system/environment.js +19 -0
- package/dist/system/environment.js.map +1 -1
- package/dist/system/factory.d.ts +2 -0
- package/dist/system/factory.js +4 -0
- package/dist/system/factory.js.map +1 -1
- package/dist/system/git.d.ts +5 -1
- package/dist/system/git.js +19 -2
- package/dist/system/git.js.map +1 -1
- package/dist/system/github.d.ts +3 -12
- package/dist/system/github.js +7 -31
- package/dist/system/github.js.map +1 -1
- package/dist/system/run-files.d.ts +14 -0
- package/dist/system/run-files.js +51 -8
- package/dist/system/run-files.js.map +1 -1
- package/package.json +2 -2
- package/prompts/describe.md +36 -15
- package/prompts/plan.md +7 -5
- package/prompts/review.md +38 -21
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ An idea becomes an issue; the operator specifies it with an agent; one run then
|
|
|
7
7
|
## Requirements
|
|
8
8
|
|
|
9
9
|
- macOS or Linux, Node.js 24 or later, npm.
|
|
10
|
-
- For the `implement` run: Git, a GitHub repository whose remote is `origin`, with `origin/HEAD` naming the main branch (`git clone` sets it; otherwise `git remote set-head origin --auto`), and the [GitHub CLI](https://cli.github.com) `gh
|
|
10
|
+
- For the `implement` run: Git, a GitHub repository whose remote is `origin`, with `origin/HEAD` naming the main branch (`git clone` sets it; otherwise `git remote set-head origin --auto`), and the [GitHub CLI](https://cli.github.com) `gh`, logged in (`gh auth login`).
|
|
11
11
|
- For agents run by Claude Code: the `claude` CLI 2.1.296 or later in the `PATH`, logged in with a claude.ai subscription (`claude`, then `/login`) or given an API key (`ANTHROPIC_API_KEY`), and a `ps` that lists processes with their group and start time (macOS's, or procps on Linux), with which each call ends what its agent's commands leave running.
|
|
12
12
|
- For agents run by Codex: the `codex` CLI 0.160.1 or later in the `PATH`, logged in with a ChatGPT account or an API key (`codex login`) or given an API key (`CODEX_API_KEY`); Codex works only in a Git repository.
|
|
13
13
|
- For agents run by Pi: the `pi` CLI (`@earendil-works/pi-coding-agent` 1.1.0 or later) in the `PATH`, connected to the provider each Pi profile names (`pi`, then `/login`, or the provider's API key).
|
|
@@ -51,7 +51,7 @@ The global `factory` command uses the nearest `.factory/` holding a `package.jso
|
|
|
51
51
|
4. **Executing.** `factory run implement --issue <slug>`: starting the run approves the spec. It runs without the operator; follow it in `factory ui`.
|
|
52
52
|
5. **To Approve → Done.** Review and merge the pull request. After `git pull`, `plan.md` is on the main branch: the issue is done. Its folder stays.
|
|
53
53
|
|
|
54
|
-
A failed run stops; past `prepare`, it leaves the repository on the branch `<slug>`, with the agent's changes, for inspection. When the cause has passed (a timeout, the budget, the network, a Git hook), or when the checks or the review stopped it at their maximum of corrections and you grant one more round, `factory resume <RUN_ID>` starts the stopped stage again (each resumption allows that loop one more correction), its agent in the same conversation: the repository must still be on `<slug>` at the commit where the run stopped (the plan's commit, or the starting commit before it), with its changes in progress and none in `.factory/`; a delivery that stopped after its commit resumes once `git reset --soft <plan commit>` puts the changes back in progress, as the refusal says, a run whose pull request is recorded in its state, stopped once its delivery was done, does not resume,
|
|
54
|
+
A failed run stops; past `prepare`, it leaves the repository on the branch `<slug>`, with the agent's changes, for inspection. When the cause has passed (a timeout, the budget, the network, a Git hook), or when the checks or the review stopped it at their maximum of corrections and you grant one more round, `factory resume <RUN_ID>` starts the stopped stage again (each resumption allows that loop one more correction), its agent in the same conversation: the repository must still be on `<slug>` at the commit where the run stopped (the plan's commit, or the starting commit before it), with its changes in progress and none in `.factory/`; a delivery that stopped after its commit resumes once `git reset --soft <plan commit>` puts the changes back in progress, as the refusal says, a run whose pull request is recorded in its state, stopped once its delivery was done, does not resume, and neither does, past `prepare`, a run started with an earlier version of the factory. Otherwise, clean up, fix what is needed (often the spec, committed and pushed again) and start a new run: it starts again from the main branch, recreates the branch and replaces the pull request's content. Requested changes on a pull request are fixed the same way, or by hand on the branch. An issue is abandoned by deleting its folder (and closing its pull request, if any).
|
|
55
55
|
|
|
56
56
|
`issue-save` lists every problem of a draft's format at once, then, for a valid spec, its dependencies that are no issue, refuses a spec for an issue that has no `issue.md`, replaces the file if it exists and commits nothing; each save is a run. The skill tells the agent never to write in `.factory/issues/` directly, commit or run `implement`. Pi reads a project's skills once the project is trusted; Codex ignores a `SKILL.md` that is itself a link, hence the folder link.
|
|
57
57
|
|
|
@@ -67,16 +67,15 @@ An issue is a folder `.factory/issues/<slug>/` holding at least one of the files
|
|
|
67
67
|
|
|
68
68
|
| Stage | What it does |
|
|
69
69
|
|---|---|
|
|
70
|
-
| `prepare` | Refuses to start, listing every reason, unless the repository is on the main branch without any change in progress, the main branch matches `origin` after `git fetch`, `spec.md` is valid (otherwise it says to fix it with `/issue <slug>`), each of its dependencies is delivered (its `plan.md` is on the main branch), the issue has no `plan.md` (an issue already delivered is never restarted), its slug does not name the main branch (its branch would replace it), `
|
|
70
|
+
| `prepare` | Refuses to start, listing every reason, unless the repository is on the main branch without any change in progress, the main branch matches `origin` after `git fetch`, `spec.md` is valid (otherwise it says to fix it with `/issue <slug>`), each of its dependencies is delivered (its `plan.md` is on the main branch), the issue has no `plan.md` (an issue already delivered is never restarted), its slug does not name the main branch (its branch would replace it), `FACTORY_UI_URL`, when set, is an http or https address, and `gh` is logged in and finds the repository on GitHub; then the repository's checks must pass on the main branch, before any agent; then, under a lock of the working tree (`factory-implement.lock` in its Git directory, held for that step only) and unless the repository moved meanwhile (another run is using it: the run stops), creates (or recreates) the branch `<slug>` from the approved start commit. Branches are read and pushed by their full reference: a tag of the same name never stands in for them |
|
|
71
71
|
| `plan`, `planned` | The `plan` agent receives the slug and the spec. It first explores the code where the spec changes it (mechanisms, invariants, conventions, test tools), handing each area of a spec that touches several to a sub-agent when its profile allows it, then answers with a plan: what the code already does that the change must keep, and steps with their criteria, the files expected, the situations to handle besides the main path and their tests. Where the spec reads more than one way, or its letter would give a behavior its objective would not want, the plan records the reading it chose, apart from its risks: the pull request asks you to confirm it. The plan says what must be true, not how to write it; the implementing agent may take a better way and records it as a deviation; every criterion must be served by a step, otherwise the plan goes back to it (2 times at most) |
|
|
72
72
|
| `commit-plan` | Writes `plan.md` and commits it alone (`docs: plan <slug>`): the run's reference commit |
|
|
73
73
|
| `implement`, `implemented` | The `implement` agent receives the spec, the plan and the run's browser, in one conversation for the run, and answers with evidence for each criterion and its deviations from the plan. It fixes what the review asks for test first: a test that reproduces the trigger and fails, the fix, then a test for each situation around it that the fix changes; what no test can show, it checks in the browser before and after; the run stops if it committed, switched branches or changed `.factory/`, whatever it answered |
|
|
74
74
|
| `verify` | The repository's checks, in order, a check with `when` only if a file changed since the reference commit matches it; a failure, a timeout or a command that cannot start goes back to `implement` with the command, the cause and the end of its output (3 times at most). What each check wrote is kept in the run's folder, for the review, and a failed check's whole output in `checks/failed-<n>-<name>.log`, the run's `n`th failure, resumptions included, whose path the run journals (over 2 MiB per stream, its end, marked as truncated) |
|
|
75
75
|
| `diff` | Every change since the reference commit, new files included, with each file's added and removed lines; the run stops if it is over 2 MiB. Over 1,500 lines of diff, the change is large: its diff is kept in the run's folder instead of being sent in the review's request. |
|
|
76
|
-
| `review`, `reviewed` | The `review` agent, in a new conversation for each review, receives the spec, the changes (the diff, or for a large change where to read it), the checks and where their output is, and the run's browser, never the implementing agent's answer. It judges each criterion `met` or `not_met`, with its decisive evidence, and gives each finding a nature, only on what it established: a test it ran, the application it tried or a code path it followed to its end; a risk it could not establish is at most a `weak_test`. From the second round on, once it has answered on its own with a review the run can judge (an answer it cannot, such as a note sent while its sub-agents still run, goes back to it as an invalid review), it receives in the same conversation the previous review and checks
|
|
77
|
-
| `describe`, `described` | Once the change is approved, the `describe` agent explains it for the
|
|
76
|
+
| `review`, `reviewed` | The `review` agent, in a new conversation for each review, receives the spec, the changes (the diff, or for a large change where to read it), the checks and where their output is, and the run's browser, never the implementing agent's answer. It judges each criterion `met` or `not_met`, with its decisive evidence, and gives each finding a nature, only on what it established: a test it ran, the application it tried or a code path it followed to its end; a risk it could not establish is at most a `weak_test`. From the second round on, once it has answered on its own with a review the run can judge (an answer it cannot, such as a note sent while its sub-agents still run, goes back to it as an invalid review), it receives in the same conversation the previous review's unmet criteria and findings and checks them itself, without sub-agents: it reads and runs the tests of each fix and answers on them alone, with each criterion judged again, the findings a faithful test proves fixed (one that reproduces the trigger, would fail without the fix and covers the situations around it), those its first answer reports again, and what the fixes broke (a fix that breaks what worked is a `regression`). The run keeps its first answer whole and adds that check to it: a previous finding stays as it was until its fix is proven, unless the first answer reports it again with a nature that blocks as much, and a criterion is met only when both answers say so. Every review judged is kept in the run. An unmet criterion or a blocking finding goes back to `implement` (4 times at most) with the other findings, to fix as well when that is safe, and the implementing agent fixes the mechanism that let a defect happen, wherever the change has it, rather than adding a guard per case |
|
|
77
|
+
| `describe`, `described` | Once the change is approved, the `describe` agent explains it for the review page (see below), takes its screenshots, and writes the decisions and the checks only a person can do. It receives the spec, the plan, its interpretations numbered `I1`…, the changes, the checks, the approved review, the deviations from the plan numbered `D1`…, the screenshots folder and the run's browser, never the implementing agent's summary. Its guide must name changed files only, each once, its screenshots must be distinct images of the folder, 10 MB at most, and each decision must name what exists: a criterion of the spec, an interpretation or a deviation, a changed file, lines the diff changes (60 at most), screenshots of the description; otherwise its answer goes back to it (2 times at most) |
|
|
78
78
|
| `deliver` | Commits the changes with the plan's conventional commit subject, force-pushes `<slug>` to `origin`, updates the branch's open pull request or opens one with `gh`, then returns to the main branch |
|
|
79
|
-
| `attach` | When there are screenshots: replaces the pull request's body with one that shows them and uploads them (`gh pr edit --attach`) |
|
|
80
79
|
|
|
81
80
|
A finding's nature alone decides whether it blocks the change: `incorrect_behavior`, `data_loss`, `outage`, `security`, `regression` and `spec_violation` block it; `weak_test`, `maintainability` and `documentation` do not.
|
|
82
81
|
|
|
@@ -108,18 +107,15 @@ The `implement`, `review` and `describe` agents check a change that shows in a w
|
|
|
108
107
|
|
|
109
108
|
#### The pull request
|
|
110
109
|
|
|
111
|
-
Its title is the commit subject. Its body
|
|
110
|
+
Its title is the commit subject. Its body is short: a warning first when the operator delivered the change despite its review (see below), a caution when merging is a one-way door (reverting does not walk it back: a migration that transforms data, a published contract, an external effect), then the link to the change's review page with what there is to decide, and a table at a glance: the files changed with their lines, links to the spec at the starting commit and to `plan.md`, the run's ID; the door and the blast radius; the criteria met, the checks and the screenshots; how the review ended and what it left. The link is `FACTORY_UI_URL` followed by `review?run=<RUN_ID>`; without it, the body says to open `/review?run=<RUN_ID>` in `factory ui` where the run ran.
|
|
112
111
|
|
|
113
|
-
|
|
114
|
-
- a table at a glance: the files changed with their lines, links to the spec at the starting commit and to `plan.md`, the run's ID; the door and the blast radius; the criteria met, the checks and the screenshots; how the review ended and what it left;
|
|
115
|
-
- **Why**: the spec's Objective;
|
|
116
|
-
- **What changes**, one item per change, a change to what already existed among them, **How it works** (Mermaid diagrams of how components interact or how data flows, sketches of a tree as a diff, pseudocode; none when the text suffices), **Merge danger** (a table: door, blast radius, rollback, what to do after the merge) and **Review guide** (the file to start with, then the files in reading order, every group's list folded when the change has more than 10 files; the run lists the others), written by the `describe` agent with as many items as the change calls for;
|
|
117
|
-
- **Needs your judgment**: the plan's readings of the spec, to confirm or to fix in the spec; the review rounds, what stays open, the non-blocking findings with their nature and the deviations from the plan; folded, what earlier reviews found and later rounds fixed, with the proof, and the plan's assumptions;
|
|
118
|
-
- **Evidence**: the screenshots of the new state, or why there are none; the criteria met; the checks passed or skipped; each criterion with the review's evidence, folded.
|
|
112
|
+
#### The review page
|
|
119
113
|
|
|
120
|
-
|
|
114
|
+
`factory ui` serves, for each run that delivered a described pull request, a page to review it from the run's data alone, linked from the pull request and from the issue's card. It is a deck of cards, one at a time, between two piles: send a card right when it suits you, left when it needs another look, by dragging it or with the keyboard (→, ←, D for its dossier, S to skip a check, Backspace to take it back). The cards to know come first: the mission and its danger (door, blast radius, rollback), what the review did (each round's criteria met and findings, the checks), what changes (each change, the size of the diff, why there is no screenshot when there is none), then, when the run took screenshots, what it looks like: each screenshot with its caption, at full size on a click, where ← and → go from one to the next. Then a card per decision: each the `describe` agent wrote by rule (a change beyond the spec, an existing test whose expectation changed, a reading of the spec beyond its letter), with its advice and what shows it (the spec's words beside the plan's reading, an excerpt of the diff, screenshots, a short list); one for the non-blocking findings the review left, to merge and note for later or not; one for what the operator delivered open. Then a card per check only a person can do (a real phone, real data, a sound), and the verdict: good to merge, with what to note and what you did not check, or changes requested, with a message made of the cards you sent back and your notes, to copy. Each card opens its dossier: the run's texts word for word, the review's evidence, links to the spec, the plan and the file in the pull request; the change's dossier also holds the diagrams, where to read the code, and every interpretation, deviation and assumption no card shows. Your answers are kept with the run (`answers.json` in its folder), so that another device resumes them.
|
|
121
115
|
|
|
122
|
-
|
|
116
|
+
To open it away from the machine of the run, publish `factory ui` on your private network, for instance with [Tailscale](https://tailscale.com): run `factory ui --port 4321` there (kept running, under a service manager or `tmux`), then `tailscale serve --bg 4321`, and set `FACTORY_UI_URL` to the address it prints (such as `https://forge.example.ts.net/`) in `.factory/.env` or the shell. Pull requests then link the page there, and `factory ui` answers that host name, which `tailscale serve` passes on.
|
|
117
|
+
|
|
118
|
+
When the review still asks for changes after its last round, the run stops; `factory resume <RUN_ID>` gives it one more round, and `factory resume <RUN_ID> --accept-review` delivers it as the review left it, on your decision: the pull request then opens with a warning, and the review page has a card for the unmet criteria and blocking findings left open. What comes from the run (the spec, the criteria, the checks, the review, the deviations, the risks) is put together by the factory, never rewritten by an agent; the implementing agent's own summary is not shown. The screenshots stay in the run's folder, where the review page shows them: nothing is uploaded to GitHub.
|
|
123
119
|
|
|
124
120
|
### The `tidy` run
|
|
125
121
|
|
|
@@ -131,7 +127,7 @@ Working fast with agents leaves waste in the code. `factory run tidy` removes it
|
|
|
131
127
|
| `survey` | Reads what the scout needs: recent code, that is the commits of the main branch since the last cleanup merged (the last commit that brought a `.factory/issues/tidy-*/plan.md` to it), or of the last 14 days before the first one, 200 at most, merges aside, with their files and how often each changed; the files of the open pull requests; the cleanups closed without being merged |
|
|
132
128
|
| `scout`, `scouted` | The `scout` agent, with the `plan` profile, explores recent code first, then the rest of the repository, compares candidates and answers with the cleanup that comes first, or none. A cleanup changes nothing observable: it removes the waste that fast changes leave (duplicates, dead code, needless defenses, functions too long to follow, departures from the conventions, stale comments and documentation, weak tests), leaves the files of the open pull requests alone and proposes no cleanup like those the operator closed. It answers with the cleanup's `issue.md` and `spec.md`: the gate reads them as `issue-save` does and refuses a dependency, since a cleanup starts from the main branch alone, otherwise the answer goes back to the scout (2 times at most). When none is worth a pull request, the run goes back to the main branch, deletes its empty branch (`release`) and ends there |
|
|
133
129
|
| `commit-spec` | Commits the cleanup's `issue.md` and `spec.md` on the run's branch (`docs: spec tidy-<run id>`) |
|
|
134
|
-
| `plan` … `
|
|
130
|
+
| `plan` … `deliver` | As in `implement`, from that spec, except that the review sends the change back 2 times at most; the pull request links the spec at its own commit |
|
|
135
131
|
|
|
136
132
|
The pull request is your approval: merge it, or close it, and the scout does not propose that cleanup again. A defect the scout finds is named in the issue, never fixed by a cleanup. Until it is merged, a cleanup lives on its branch only, so the Kanban does not show it: follow its run under `/runs/`; once merged, it is an issue like the others, in Done. The run needs the repository on its main branch without changes, as `implement` does: run it from a clone kept for it rather than the one you work in. A cleanup is named after its run: its branch and its folder `.factory/issues/tidy-<run id>/` lead to the run under `/runs/`, and its title is that of its pull request and `issue.md`. A failed run stops on its branch and resumes as `implement` does.
|
|
137
133
|
|
|
@@ -148,7 +144,7 @@ The pull request is your approval: merge it, or close it, and the scout does not
|
|
|
148
144
|
| To Approve | the last `implement` run completed, and `plan.md` is not on the main branch |
|
|
149
145
|
| Done | `plan.md` is on the main branch, a merged cleanup's too |
|
|
150
146
|
|
|
151
|
-
The files are read in the working tree, the main branch is the local branch `origin/HEAD` names, and runs are those of `.factory/runs/`. A card shows the issue's title (the `# ` line of `issue.md`; its slug without one), its slug, the dependencies of its spec not delivered yet (none once the issue is delivered), the newest pull request its `implement` runs recorded (a new run updates the same one) and, for each workflow shown on cards, its last run on the issue: its steps (done, current, failed or to come, each with the corrections that sent the run back into it), its duration and its estimated cost. `implement` shows six steps: prepare, plan, implement, verify, review and deliver (the description
|
|
147
|
+
The files are read in the working tree, the main branch is the local branch `origin/HEAD` names, and runs are those of `.factory/runs/`. A card shows the issue's title (the `# ` line of `issue.md`; its slug without one), its slug, the dependencies of its spec not delivered yet (none once the issue is delivered), the newest pull request its `implement` runs recorded (a new run updates the same one), with a Review link to its review page when the run described it and, for each workflow shown on cards, its last run on the issue: its steps (done, current, failed or to come, each with the corrections that sent the run back into it), its duration and its estimated cost. `implement` shows six steps: prepare, plan, implement, verify, review and deliver (the description included). The title opens the newest of these runs in the runs dashboard. The most recently active issue comes first in its column; the page refreshes every 2 seconds. What may leave the board incomplete is listed above it: a `factory.ts` that does not load (`factory ui` loads it when it starts: restart it once fixed), an unreadable `issue.md` or run, an undelivered issue's `spec.md` that cannot be read or is invalid (its dependencies are then not shown), or a main branch Git cannot read (`origin/HEAD` missing: `git remote set-head origin --auto` sets it).
|
|
152
148
|
|
|
153
149
|
## Adapt the factory
|
|
154
150
|
|
|
@@ -327,7 +323,7 @@ The run executes in the foreground. The terminal first shows the launch (flow's
|
|
|
327
323
|
|
|
328
324
|
**Resume.** `factory resume RUN_ID` runs a failed run again in the foreground, under the same ID, at the stage where it stopped (with `--accept-review`, an `implement` or `tidy` run its review stopped by asking for changes is delivered as the review left it: see The pull request): its input, data, corrections, cost and agents' conversations are kept, the stage's journal starts with why the run had stopped (`Resumed after: …`), and its agent is told so and asked to continue rather than start over. The workflow is the one `factory.ts` defines now, its prompts are read again, and its runtimes are checked as for `factory run`. The budget is the one given with `--max-cost-usd`, else the run's own, else the workflow's; a run that already spent it does not resume without a higher one. The run's budget still counts its whole cost, but calls that announced no cost before the resumption, such as a call stopped by its timeout, no longer fail it; their cost counts only if the runtime includes it in the total it later announces for the resumed session, and the run's cost stays marked partial. The run's duration leaves out the time it spent stopped. Only a failed run resumes, and by one command at a time: a run left `running` after a second Ctrl-C does not, and a workflow may refuse to resume from the state of the repository, as `implement` does. A resumed stage starts over: what it did before it failed must be safe to do again. A run stopped by a correction maximum resumes at the stage that asked for it, which may then take one more correction: the checks may pass after a fix by hand, and a gate judging the same answer sends it back once more.
|
|
329
325
|
|
|
330
|
-
`factory status` lists the repository's runs or shows one; while `factory.ts` cannot load, `status` and `ui` say so and still show the runs with the default rendering. `factory ui` serves on `127.0.0.1` (port chosen by the system unless `--port` sets it, URL printed; Ctrl-C stops it) the Kanban on
|
|
326
|
+
`factory status` lists the repository's runs or shows one; while `factory.ts` cannot load, `status` and `ui` say so and still show the runs with the default rendering. `factory ui` serves on `127.0.0.1` (port chosen by the system unless `--port` sets it, URL printed; Ctrl-C stops it) the Kanban on `/`, each delivered run's review page at `/review?run=<RUN_ID>` (see The review page; it also answers the host of `FACTORY_UI_URL`), and, under `/runs/`, a read-only dashboard of the repository's runs, with a Board link back to the Kanban: the runs in a rail grouped by day, named by their issue, filtered and searched; for a run its figures and links above three tabs: Timeline with its launches on one bar with the pauses between them and why each stopped, each opening what it ran with and what changed since the one before, its stage executions in lanes over its active time with its rounds and corrections, then the selected execution's panel (Overview with the session each call ran in as its runtime confirmed it, Input with the correction it received and what flow sent each agent call, loaded on demand: the workflow's prompt, the instructions, the context and the output schema, the whole request and the command, Output with the agent's answer, the correction sent back or not and each call's answer when it made several, Activity with its calls, tool calls, journal and warnings in one timeline), Cost with its cost by session and over time, Result with its result, its links and its data as a tree. The address keeps the run, its tab, the execution chosen and the execution's tab. It follows a running run as it goes. To watch on another machine, forward the same port through SSH: `ssh -L 4321:127.0.0.1:4321 machine`, run `factory ui --port 4321` there, and open `http://localhost:4321/`. `factory doctor` checks Node, loads the workflows, reads their prompts, lists each workflow's options and checks each runtime's executable, version and connection, without any provider request.
|
|
331
327
|
|
|
332
328
|
## Data
|
|
333
329
|
|
|
@@ -342,7 +338,7 @@ Tokens and costs are the figures each runtime announces: Claude Code announces a
|
|
|
342
338
|
- The repository is used in place: one `implement` run at a time, since it needs a clean tree and stops if another run moved the repository during its starting checks; after a failure, the operator cleans up, or leaves the branch as it is to resume the run. The repository's Git hooks run on the run's commits, and a failing hook fails the run.
|
|
343
339
|
- The factory's own agents (`plan`, `implement`, `review`, `describe`) cannot run on Codex, since `maxCostUsd` is required and Codex announces no cost; the repository's own workflows and the `issue` skill can use Codex.
|
|
344
340
|
- Runtimes are not sandboxes: the implementing agent's tools can run any command its profile allows, bounded only by its prompt, the repository's agent instructions and the run's checks.
|
|
345
|
-
- The
|
|
341
|
+
- The review page lives on the machine of the run: elsewhere, the pull request shows only its header. It is served without sign-in: anyone who reaches `factory ui`, on that machine or through `FACTORY_UI_URL`, can read it and change its answers. The factory does not check the Mermaid syntax: the page shows the text of a diagram it cannot draw. Mermaid, built into the page, brings `@clemrig/factory-ui` to about 5.5 MB.
|
|
346
342
|
- Screenshots show the new state only, never the previous one; no videos.
|
|
347
343
|
- `tidy` has no scheduler of its own: cron, launchd or a CI runner starts it. Its scout reads at most 200 recent commits and the latest 100 open and 100 closed pull requests, with the first 100 files of each open one, as `gh` lists them, and a defect it finds is only named in the cleanup's issue.
|
|
348
344
|
- Not in this version: changing the factory's prompts, correction maximums, pull request format or columns from a repository; columns added by a workflow; backlog priority; actions or drag-and-drop on the board; resuming a run left `running` after its process was killed, detached runs, logs `--follow` and stop; local settings or profile overrides; graph drawing in the dashboard; runs or issues of several repositories; `factory new`; a shared stage library; a test harness for workflows; CI, cron or Git hook triggers; notifications; Windows.
|
package/dist/app/factory.d.ts
CHANGED
package/dist/app/factory.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loadLocalEnvironment } from "../system/environment.js";
|
|
1
|
+
import { loadLocalEnvironment, uiUrl } from "../system/environment.js";
|
|
2
2
|
import { emptyFactory, importFactory, } from "../system/factory.js";
|
|
3
3
|
import { workflowsOf } from "./define.js";
|
|
4
4
|
import { optionProblems } from "./options.js";
|
|
@@ -38,4 +38,8 @@ export async function openFactoryForReading(factoryDir) {
|
|
|
38
38
|
return { factory: emptyFactory(factoryDir), error: message(error) };
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
/** Where others reach `factory ui`, when `FACTORY_UI_URL` says it. */
|
|
42
|
+
export function uiAddress() {
|
|
43
|
+
return uiUrl();
|
|
44
|
+
}
|
|
41
45
|
//# sourceMappingURL=factory.js.map
|
package/dist/app/factory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/app/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/app/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EACL,YAAY,EAEZ,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAI9C,MAAM,OAAO,GAAG,CAAC,KAAc,EAAE,EAAE,CACjC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAEzD,iGAAiG;AACjG,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB;IACpD,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ;QACX,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IACzC,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC;QACxD,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjD,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,IAAI;QAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC1E,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,QAAQ,CAAC,MAAM;YACjB,MAAM,IAAI,KAAK,CACb,oBAAoB,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5D,CAAC;IACN,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAAkB;IAClD,oBAAoB,CAAC,UAAU,CAAC,CAAC;IACjC,OAAO,aAAa,CAAC,UAAU,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB;IAElB,oBAAoB,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;IACtD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IACtE,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,SAAS;IACvB,OAAO,KAAK,EAAE,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReviewAnswers, type ReviewPage } from "../domain/review-page.js";
|
|
2
|
+
import { type Factory } from "../system/factory.js";
|
|
3
|
+
/**
|
|
4
|
+
* The review page of run `id`, from its data; `undefined` when it delivered
|
|
5
|
+
* no described pull request. `screenshotUrl` says where the page loads one.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readReviewPage(factory: Factory, id: string, screenshotUrl: (file: string) => string): Promise<ReviewPage | undefined>;
|
|
8
|
+
/** One of the screenshots the run's description lists; `undefined` for any other file. */
|
|
9
|
+
export declare function readReviewScreenshot(factory: Factory, id: string, file: string): Promise<{
|
|
10
|
+
body: Uint8Array;
|
|
11
|
+
type: string;
|
|
12
|
+
} | undefined>;
|
|
13
|
+
/** Where a person is in the run's review; `undefined` before the first answer, or when what is kept no longer reads. */
|
|
14
|
+
export declare function readReviewAnswers(factory: Factory, id: string): Promise<ReviewAnswers | undefined>;
|
|
15
|
+
/** Keeps the answers the page sent for the run's review; returns why they are refused, if they are. */
|
|
16
|
+
export declare function saveReviewAnswers(factory: Factory, id: string, answers: unknown): Promise<string | undefined>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { summarizeRun, UnknownRunError, z } from "@clemrig/flow";
|
|
2
|
+
import { maxScreenshotBytes } from "../domain/description.js";
|
|
3
|
+
import { reviewAnswers, reviewPage, } from "../domain/review-page.js";
|
|
4
|
+
import { runDir } from "../system/factory.js";
|
|
5
|
+
import { fileAnchor } from "../system/github.js";
|
|
6
|
+
import { keepAnswers, readAnswers, readScreenshot, } from "../system/run-files.js";
|
|
7
|
+
import { loadRun } from "./runs.js";
|
|
8
|
+
import { diffOf } from "./workflows/stages/deliver.js";
|
|
9
|
+
import { facts, specCommitOf } from "./workflows/stages/run.js";
|
|
10
|
+
/** The run, when it delivered a described pull request; `undefined` otherwise, an unknown ID included. */
|
|
11
|
+
async function delivered(factory, id) {
|
|
12
|
+
let state;
|
|
13
|
+
try {
|
|
14
|
+
state = await loadRun(factory, id);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error instanceof UnknownRunError)
|
|
18
|
+
return undefined;
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
const { pullRequest, description } = state.data;
|
|
22
|
+
return typeof pullRequest === "string" && typeof description === "object"
|
|
23
|
+
? state
|
|
24
|
+
: undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The review page of run `id`, from its data; `undefined` when it delivered
|
|
28
|
+
* no described pull request. `screenshotUrl` says where the page loads one.
|
|
29
|
+
*/
|
|
30
|
+
export async function readReviewPage(factory, id, screenshotUrl) {
|
|
31
|
+
const state = await delivered(factory, id);
|
|
32
|
+
if (!state)
|
|
33
|
+
return undefined;
|
|
34
|
+
const f = facts(state);
|
|
35
|
+
const { durationMs, costUsd } = summarizeRun(state);
|
|
36
|
+
return reviewPage({
|
|
37
|
+
run: { id, durationMs, costUsd },
|
|
38
|
+
title: f.title,
|
|
39
|
+
objective: f.objective,
|
|
40
|
+
criteria: f.criteria,
|
|
41
|
+
plan: f.plan,
|
|
42
|
+
implementation: f.implementation,
|
|
43
|
+
review: f.review,
|
|
44
|
+
...(f.reviews ? { reviews: f.reviews } : {}),
|
|
45
|
+
acceptedReview: f.acceptedReview === true,
|
|
46
|
+
checks: f.checks,
|
|
47
|
+
skippedChecks: f.skippedChecks,
|
|
48
|
+
files: f.changes.files,
|
|
49
|
+
diff: await diffOf(f),
|
|
50
|
+
description: f.description,
|
|
51
|
+
pullRequest: f.pullRequest,
|
|
52
|
+
links: {
|
|
53
|
+
spec: `${f.repository.url}/blob/${specCommitOf(f)}/${f.specPath}`,
|
|
54
|
+
plan: `${f.repository.url}/blob/${f.planCommit}/${f.planPath}`,
|
|
55
|
+
},
|
|
56
|
+
fileUrl: (path) => fileAnchor(f.pullRequest, path),
|
|
57
|
+
screenshotUrl,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** One of the screenshots the run's description lists; `undefined` for any other file. */
|
|
61
|
+
export async function readReviewScreenshot(factory, id, file) {
|
|
62
|
+
const state = await delivered(factory, id);
|
|
63
|
+
if (!state)
|
|
64
|
+
return undefined;
|
|
65
|
+
const { description, screenshotsDir } = facts(state);
|
|
66
|
+
if (!description.screenshots.some((shot) => shot.file === file))
|
|
67
|
+
return undefined;
|
|
68
|
+
return readScreenshot(screenshotsDir, file, maxScreenshotBytes);
|
|
69
|
+
}
|
|
70
|
+
/** Where a person is in the run's review; `undefined` before the first answer, or when what is kept no longer reads. */
|
|
71
|
+
export async function readReviewAnswers(factory, id) {
|
|
72
|
+
if (!(await delivered(factory, id)))
|
|
73
|
+
return undefined;
|
|
74
|
+
const kept = reviewAnswers.safeParse(await readAnswers(runDir(factory, id)));
|
|
75
|
+
return kept.success ? kept.data : undefined;
|
|
76
|
+
}
|
|
77
|
+
/** Keeps the answers the page sent for the run's review; returns why they are refused, if they are. */
|
|
78
|
+
export async function saveReviewAnswers(factory, id, answers) {
|
|
79
|
+
if (!(await delivered(factory, id)))
|
|
80
|
+
return `Run ${id} has no change to review`;
|
|
81
|
+
const parsed = reviewAnswers.safeParse(answers);
|
|
82
|
+
if (!parsed.success)
|
|
83
|
+
return `Invalid answers:\n${z.prettifyError(parsed.error)}`;
|
|
84
|
+
await keepAnswers(runDir(factory, id), parsed.data);
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/app/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineWorkflow, END, node, retry, z } from "@clemrig/flow";
|
|
2
2
|
import { DELIVERY } from "../../domain/delivery.js";
|
|
3
3
|
import { isSlug } from "../../domain/issue.js";
|
|
4
|
-
import {
|
|
4
|
+
import { deliver, deliveryResult, describer, judgeDescription, } from "./stages/deliver.js";
|
|
5
5
|
import { implementer, judgeImplementation } from "./stages/implement.js";
|
|
6
6
|
import { commitPlan, judgePlan, planner } from "./stages/plan.js";
|
|
7
7
|
import { prepareIssue } from "./stages/prepare.js";
|
|
@@ -27,10 +27,7 @@ export function implement(definition) {
|
|
|
27
27
|
{ name: "implement", stages: ["implement", "implemented"] },
|
|
28
28
|
{ name: "verify", stages: ["verify"] },
|
|
29
29
|
{ name: "review", stages: ["diff", "review", "reviewed"] },
|
|
30
|
-
{
|
|
31
|
-
name: "deliver",
|
|
32
|
-
stages: ["describe", "described", "deliver", "attach"],
|
|
33
|
-
},
|
|
30
|
+
{ name: "deliver", stages: ["describe", "described", "deliver"] },
|
|
34
31
|
],
|
|
35
32
|
nodes: {
|
|
36
33
|
prepare: node(prepareIssue(definition.verify), { ok: "plan" }),
|
|
@@ -66,8 +63,7 @@ export function implement(definition) {
|
|
|
66
63
|
ok: "deliver",
|
|
67
64
|
invalid: retry("describe", 2),
|
|
68
65
|
}),
|
|
69
|
-
deliver: node(deliver, { done: END
|
|
70
|
-
attach: node(attach, { ok: END }),
|
|
66
|
+
deliver: node(deliver, { done: END }),
|
|
71
67
|
},
|
|
72
68
|
resumeProblem,
|
|
73
69
|
// Also called for a failed run, which opened no pull request.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"implement.js","sourceRoot":"","sources":["../../../src/app/workflows/implement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"implement.js","sourceRoot":"","sources":["../../../src/app/workflows/implement.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { type AgentProfile, type RunState, type Stage } from "@clemrig/flow";
|
|
2
2
|
import type { AppDeclaration } from "../../../domain/app.js";
|
|
3
|
+
import { type Facts } from "./run.js";
|
|
3
4
|
/**
|
|
4
5
|
* The describing agent: once the change is approved, it explains it to a
|
|
5
6
|
* reader who forgot its issue, and takes its screenshots. It never reads the
|
|
6
7
|
* implementing agent's account of its own work.
|
|
7
8
|
*/
|
|
8
9
|
export declare function describer(profile: AgentProfile, app?: AppDeclaration): Stage<"ok" | "modified">;
|
|
9
|
-
/**
|
|
10
|
+
/** The change's whole diff: a large one, from the file the review read. */
|
|
11
|
+
export declare function diffOf({ changes }: Facts): Promise<string>;
|
|
12
|
+
/** Changed files only in the guide and the decisions, distinct screenshots of their folder. */
|
|
10
13
|
export declare const judgeDescription: Stage<"invalid" | "ok">;
|
|
11
14
|
/**
|
|
12
15
|
* Commits the changes with the plan's subject, force-pushes the branch, opens
|
|
13
16
|
* or updates its pull request, then returns to the main branch.
|
|
14
17
|
*/
|
|
15
|
-
export declare const deliver: Stage<"
|
|
16
|
-
/** Uploads the screenshots into the open pull request's body. */
|
|
17
|
-
export declare const attach: Stage<"ok">;
|
|
18
|
+
export declare const deliver: Stage<"done">;
|
|
18
19
|
/** The delivered pull request, its review, checks and merge danger; nothing before it is open. */
|
|
19
20
|
export declare function deliveryResult(state: Readonly<RunState>): string[];
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { agent, code, gate,
|
|
1
|
+
import { agent, code, gate, z, } from "@clemrig/flow";
|
|
2
2
|
import { descriptionOutput, descriptionProblems, maxScreenshotBytes, } from "../../../domain/description.js";
|
|
3
|
-
import {
|
|
3
|
+
import { mergeDangerLine, pullRequestBody, reviewPath, } from "../../../domain/pull-request.js";
|
|
4
|
+
import { uiUrl } from "../../../system/environment.js";
|
|
4
5
|
import * as git from "../../../system/git.js";
|
|
5
6
|
import * as github from "../../../system/github.js";
|
|
6
|
-
import { screenshotProblems } from "../../../system/run-files.js";
|
|
7
|
+
import { readKept, screenshotProblems } from "../../../system/run-files.js";
|
|
7
8
|
import { browserContext, readers, reading, runningApp } from "./agents.js";
|
|
8
|
-
import { facts,
|
|
9
|
+
import { facts, reviewRounds, short, specCommitOf } from "./run.js";
|
|
9
10
|
/** One conversation for the description, a new one once it changed the repository. */
|
|
10
11
|
function describeSession(state) {
|
|
11
12
|
const restarts = state.retries?.["describe.modified"] ?? 0;
|
|
@@ -23,30 +24,35 @@ export function describer(profile, app) {
|
|
|
23
24
|
session: describeSession,
|
|
24
25
|
context: ({ state }) => {
|
|
25
26
|
const f = facts(state);
|
|
27
|
+
// Numbered, so that a decision names the one it comes from.
|
|
28
|
+
const numbered = (prefix, texts) => texts.map((text, index) => ({ id: `${prefix}${index + 1}`, text }));
|
|
26
29
|
return {
|
|
27
30
|
issue: f.slug,
|
|
28
31
|
spec: f.spec,
|
|
29
32
|
plan: f.plan,
|
|
33
|
+
interpretations: numbered("I", f.plan.interpretations),
|
|
30
34
|
changes: f.changes,
|
|
31
35
|
checks: f.checks,
|
|
32
36
|
review: f.review,
|
|
33
|
-
deviations: f.implementation.deviations,
|
|
34
|
-
repository: { public: f.repository.public },
|
|
37
|
+
deviations: numbered("D", f.implementation.deviations),
|
|
35
38
|
screenshots: f.screenshotsDir,
|
|
36
39
|
...browserContext(state),
|
|
37
40
|
};
|
|
38
41
|
},
|
|
39
42
|
})));
|
|
40
43
|
}
|
|
41
|
-
/**
|
|
42
|
-
function
|
|
44
|
+
/** Where others open the run's review page, when `FACTORY_UI_URL` says where `factory ui` is. */
|
|
45
|
+
function reviewPage(runId) {
|
|
46
|
+
const base = uiUrl();
|
|
47
|
+
return base && new URL(reviewPath(runId).slice(1), base).href;
|
|
48
|
+
}
|
|
49
|
+
/** What the pull request shows, from the run's data. */
|
|
50
|
+
function pullRequestFacts(state) {
|
|
43
51
|
const f = facts(state);
|
|
52
|
+
const page = reviewPage(state.id);
|
|
44
53
|
return {
|
|
45
|
-
objective: f.objective,
|
|
46
54
|
criteria: f.criteria,
|
|
47
55
|
deviations: f.implementation.deviations,
|
|
48
|
-
interpretations: f.plan.interpretations,
|
|
49
|
-
assumptions: f.plan.risks,
|
|
50
56
|
review: f.review,
|
|
51
57
|
acceptedReview: f.acceptedReview === true,
|
|
52
58
|
reviewRounds: reviewRounds(state),
|
|
@@ -54,16 +60,22 @@ function pullRequestFacts(state, description, pending) {
|
|
|
54
60
|
checks: f.checks,
|
|
55
61
|
skippedChecks: f.skippedChecks,
|
|
56
62
|
files: f.changes.files,
|
|
57
|
-
description,
|
|
58
|
-
screenshotsPending: pending,
|
|
63
|
+
description: f.description,
|
|
59
64
|
links: {
|
|
60
65
|
spec: `${f.repository.url}/blob/${specCommitOf(f)}/${f.specPath}`,
|
|
61
66
|
plan: `${f.repository.url}/blob/${f.planCommit}/${f.planPath}`,
|
|
62
67
|
},
|
|
63
68
|
runId: state.id,
|
|
69
|
+
...(page ? { reviewPage: page } : {}),
|
|
64
70
|
};
|
|
65
71
|
}
|
|
66
|
-
/**
|
|
72
|
+
/** The change's whole diff: a large one, from the file the review read. */
|
|
73
|
+
export function diffOf({ changes }) {
|
|
74
|
+
return changes.large
|
|
75
|
+
? readKept(changes.diffFile)
|
|
76
|
+
: Promise.resolve(changes.diff);
|
|
77
|
+
}
|
|
78
|
+
/** Changed files only in the guide and the decisions, distinct screenshots of their folder. */
|
|
67
79
|
export const judgeDescription = gate(["ok", "invalid"], async ({ state }) => {
|
|
68
80
|
const parsed = descriptionOutput.safeParse(state.output);
|
|
69
81
|
if (!parsed.success)
|
|
@@ -72,14 +84,17 @@ export const judgeDescription = gate(["ok", "invalid"], async ({ state }) => {
|
|
|
72
84
|
reason: `Invalid structured output:\n${z.prettifyError(parsed.error)}`,
|
|
73
85
|
};
|
|
74
86
|
const description = parsed.data;
|
|
75
|
-
const
|
|
87
|
+
const f = facts(state);
|
|
76
88
|
const problems = [
|
|
77
|
-
...descriptionProblems(description,
|
|
78
|
-
|
|
89
|
+
...descriptionProblems(description, {
|
|
90
|
+
files: f.changes.files,
|
|
91
|
+
diff: await diffOf(f),
|
|
92
|
+
criteria: f.criteria,
|
|
93
|
+
interpretations: f.plan.interpretations.length,
|
|
94
|
+
deviations: f.implementation.deviations.length,
|
|
95
|
+
}),
|
|
96
|
+
...(await screenshotProblems(f.screenshotsDir, description.screenshots.map(({ file }) => file), maxScreenshotBytes)),
|
|
79
97
|
];
|
|
80
|
-
const length = pullRequestBody(pullRequestFacts(state, description, false)).length;
|
|
81
|
-
if (length > maxBodyLength)
|
|
82
|
-
problems.push(`The pull request's body would have ${length} characters, over ${maxBodyLength}: shorten the description`);
|
|
83
98
|
if (problems.length > 0)
|
|
84
99
|
return {
|
|
85
100
|
outcome: "invalid",
|
|
@@ -98,52 +113,30 @@ export const judgeDescription = gate(["ok", "invalid"], async ({ state }) => {
|
|
|
98
113
|
* Commits the changes with the plan's subject, force-pushes the branch, opens
|
|
99
114
|
* or updates its pull request, then returns to the main branch.
|
|
100
115
|
*/
|
|
101
|
-
export const deliver = code(["done"
|
|
102
|
-
const { slug, base, plan, specPath
|
|
116
|
+
export const deliver = code(["done"], async ({ dir, state, log }) => {
|
|
117
|
+
const { slug, base, plan, specPath } = facts(state);
|
|
103
118
|
const commit = await git.commit(dir, `${plan.commit}\n\nSpec: ${specPath}`);
|
|
104
119
|
log(`Committed "${plan.commit}" as ${short(commit)}`);
|
|
105
120
|
await git.forcePush(dir, slug);
|
|
106
121
|
log(`Pushed ${slug}, replacing any earlier version of the branch`);
|
|
107
|
-
// Its screenshots are attached once it is open: a failed upload leaves the
|
|
108
|
-
// pull request recorded, and resuming attaches them.
|
|
109
|
-
const attach = description.screenshots.length > 0;
|
|
110
122
|
const pullRequest = await github.openPullRequest(dir, {
|
|
111
123
|
base,
|
|
112
124
|
head: slug,
|
|
113
125
|
title: plan.commit,
|
|
114
|
-
body: pullRequestBody(pullRequestFacts(state
|
|
126
|
+
body: pullRequestBody(pullRequestFacts(state)),
|
|
115
127
|
});
|
|
116
128
|
log(`Pull request: ${pullRequest}`);
|
|
117
129
|
await git.switchTo(dir, base);
|
|
118
130
|
log(`Back on ${base}`);
|
|
119
131
|
return {
|
|
120
|
-
outcome:
|
|
121
|
-
reason:
|
|
122
|
-
? "Pull request open, its screenshots to attach"
|
|
123
|
-
: "Pull request open",
|
|
132
|
+
outcome: "done",
|
|
133
|
+
reason: "Pull request open",
|
|
124
134
|
data: { pullRequest },
|
|
125
135
|
};
|
|
126
136
|
});
|
|
127
|
-
/** Uploads the screenshots into the open pull request's body. */
|
|
128
|
-
export const attach = step(async ({ dir, state, log }) => {
|
|
129
|
-
const { pullRequest, description, screenshotsDir } = facts(state);
|
|
130
|
-
const files = description.screenshots.map(({ file }) => file);
|
|
131
|
-
try {
|
|
132
|
-
await github.attachImages(screenshotsDir, pullRequest, pullRequestBody(pullRequestFacts(state, description, false)), files);
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
// gh writes the body even when only some uploads succeeded: the pull
|
|
136
|
-
// request says again that its screenshots are coming.
|
|
137
|
-
await github
|
|
138
|
-
.editBody(dir, pullRequest, pullRequestBody(pullRequestFacts(state, description, true)))
|
|
139
|
-
.catch((restore) => log(`Could not put back the pull request's body: ${message(restore)}`));
|
|
140
|
-
throw error;
|
|
141
|
-
}
|
|
142
|
-
log(`Attached ${files.length} screenshot${files.length === 1 ? "" : "s"} to ${pullRequest}`);
|
|
143
|
-
});
|
|
144
137
|
/** The delivered pull request, its review, checks and merge danger; nothing before it is open. */
|
|
145
138
|
export function deliveryResult(state) {
|
|
146
|
-
const { pullRequest, checks, description, acceptedReview
|
|
139
|
+
const { pullRequest, checks, description, acceptedReview } = facts(state);
|
|
147
140
|
if (typeof pullRequest !== "string")
|
|
148
141
|
return [];
|
|
149
142
|
const rounds = reviewRounds(state);
|
|
@@ -152,16 +145,17 @@ export function deliveryResult(state) {
|
|
|
152
145
|
: `after ${rounds} round${rounds === 1 ? "" : "s"} of changes`;
|
|
153
146
|
return [
|
|
154
147
|
`Pull request: ${pullRequest}`,
|
|
148
|
+
// Runs delivered by an earlier factory have no description, nor its page.
|
|
149
|
+
...(description
|
|
150
|
+
? [
|
|
151
|
+
`Review page: ${reviewPage(state.id) ?? `${reviewPath(state.id)} in factory ui`}`,
|
|
152
|
+
]
|
|
153
|
+
: []),
|
|
155
154
|
acceptedReview
|
|
156
155
|
? `Review: delivered by the operator with open findings ${after}`
|
|
157
156
|
: `Review: approved ${after}`,
|
|
158
157
|
`Checks passed: ${checks.map(({ name }) => name).join(", ")}`,
|
|
159
|
-
// Runs delivered by an earlier factory have no description, and their
|
|
160
|
-
// plans no interpretations.
|
|
161
158
|
...(description ? [mergeDangerLine(description)] : []),
|
|
162
|
-
...(plan?.interpretations?.length
|
|
163
|
-
? [interpretationsLine(plan.interpretations.length)]
|
|
164
|
-
: []),
|
|
165
159
|
];
|
|
166
160
|
}
|
|
167
161
|
//# sourceMappingURL=deliver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliver.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/deliver.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,IAAI,EACJ,IAAI,EAGJ,
|
|
1
|
+
{"version":3,"file":"deliver.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/deliver.ts"],"names":[],"mappings":"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"}
|