@clemrig/factory 0.10.0 → 0.12.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
@@ -70,17 +70,17 @@ An issue is a folder `.factory/issues/<slug>/` holding at least one of the files
70
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), `gh` is 2.99.0 or later, 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
- | `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; the run stops if it committed, switched branches or changed `.factory/`, whatever it answered |
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, it receives in the same conversation the previous review: it checks each earlier criterion and finding against the code, lists those now fixed with their proof, keeps those that still hold, with the same nature, judges how they were fixed, tries each fix in the situations around it (a fix that breaks what worked is a `regression`), and answers its whole review again. 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 |
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 it itself, without sub-agents: it reads and runs the tests of each fix, lists as fixed those a faithful test proves (one that reproduces the trigger, would fail without the fix and covers the situations around it), keeps the others with the same nature, a fix no test proves included, judges how they were fixed (a fix that breaks what worked is a `regression`), and answers its whole review again. 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
77
  | `describe`, `described` | Once the change is approved, the `describe` agent explains it for the pull request (see below) and takes its screenshots. It receives the spec, the plan, the changes, the checks, the approved review, the deviations from the plan, whether the repository is public, 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 the pull request's body must stay under 60,000 characters; 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
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
80
 
81
81
  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
82
 
83
- If its profile allows it, the reviewer runs commands (tests, reproductions, the application in a browser) and, on a large change or one that spans several applications or layers, splits the whole review between sub-agents (the `Agent` tool), keeping no part for itself: each takes one part, its browser checks included, and one more, always, the paths across them: failures, interruptions, concurrency, recovery. It reports a defect once with every place it occurs, so that it is fixed everywhere at once. Neither the reviewer nor the describing agent changes the repository:
83
+ If its profile allows it, the reviewer runs commands (tests, reproductions, the application in a browser) and, on a large change or one that spans several applications or layers, splits its first answer between sub-agents (the `Agent` tool), keeping no part for itself: each takes one part, its browser checks included, and one more, always, the paths across them: failures, interruptions, concurrency, recovery. It reports a defect once with every place it occurs, so that it is fixed everywhere at once. Neither the reviewer nor the describing agent changes the repository:
84
84
 
85
85
  - The `diff` stage records the tree it reviews, and the review and the description always start from it: what an interrupted call left behind is put back first, on a resumed run too, and counts as that call's change. A resumed review or description whose tree Git has since pruned is refused.
86
86
  - After each call, even a failed one, what the agent changed is put back (`.gitignore` files first, so that an ignored file is never taken for an addition), and it starts again in a new conversation, once per run.
@@ -288,7 +288,7 @@ Workflows are TypeScript run directly by Node: use erasable syntax only (no `enu
288
288
  | other | `permissionMode` (a `--permission-mode` choice), `mcpServers: { name: { command, args? } }` | `sandbox`: `read-only`, `workspace-write` or `danger-full-access` | `skills`, `extensions` (paths) |
289
289
  | `timeoutMs` | default 15 minutes, no maximum | default 15 minutes, no maximum | default 15 minutes, no maximum |
290
290
 
291
- Each agent runs with this machine's and this repository's configuration, as you would run it yourself: Claude Code with its settings, `CLAUDE.md`, plugins, hooks, skills, MCP servers and memory; Codex with its `config.toml`, `AGENTS.md` files, skills and MCP servers; Pi with its extensions, skills, context files and MCP servers. The profile only adds its settings; without `tools`, `permissionMode` or `sandbox`, the agent's own configuration decides. The same workflow can therefore behave differently on another machine, and a repository's own hooks and MCP servers run with its agents. Relative skill, extension and MCP command paths are relative to `.factory/`; a bare command is looked up in the `PATH`. Runtimes are not sandboxes unless one is enabled: their tools act with your rights.
291
+ Each agent runs with this machine's and this repository's configuration, as you would run it yourself: Claude Code with its settings, `CLAUDE.md`, plugins, hooks, skills, MCP servers and memory; Codex with its `config.toml`, `AGENTS.md` files, skills and MCP servers; Pi with its extensions, skills, context files and MCP servers. The profile only adds its settings; without `tools`, `permissionMode` or `sandbox`, the agent's own configuration decides. A Claude Code call waits for its agent's background work, sub-agents included, until it ends or the profile's timeout stops the call: print mode alone would stop it after 10 minutes without a turn. The same workflow can therefore behave differently on another machine, and a repository's own hooks and MCP servers run with its agents. Relative skill, extension and MCP command paths are relative to `.factory/`; a bare command is looked up in the `PATH`. Runtimes are not sandboxes unless one is enabled: their tools act with your rights.
292
292
 
293
293
  **Prompts.** An agent's prompt is `.factory/workflows/<workflow>/prompts/<name>.md`, where the name is the stage's unless `agent({ prompt })` names another; the factory's own workflows read theirs from the package. It is appended to the runtime's system prompt (Codex receives it as the session's `developer_instructions`); the request (context and output JSON Schema) follows on standard input. Prompts are read when a run starts: an edit applies to the next run, without building anything.
294
294
 
@@ -312,7 +312,7 @@ The run executes in the foreground. The terminal shows each stage's start and en
312
312
 
313
313
  **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.
314
314
 
315
- `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 `/` and, under `/runs/`, a read-only dashboard of the repository's runs: the runs with workflow, status, current stage, duration, estimated cost and last update, and for a run its runtimes, its launches (the first one, then each resumption), each with a strip of its stages sized by duration, then its stages beside the selected one's detail (start, duration, outcome, the agent call's model, settings, tools and tokens as they come, a folded Output section with the agent's answer and the correction sent back or not, the journal and the tool calls), the correction counters and the result. 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.
315
+ `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 `/` 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, its launches on one bar with the pauses between them and why each stopped, its stage executions in lanes over its active time with its rounds and corrections, then the selected execution's panel (Overview, Input with the correction it received, Output with the agent's answer and the correction sent back or not, Activity with its calls, tool calls, journal and warnings in one timeline), beside its cost by session and over time, its result with its links and its data as a tree. The address keeps the run, the execution and the 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.
316
316
 
317
317
  ## Data
318
318
 
@@ -9,7 +9,10 @@ export declare const diff: Stage<"ok">;
9
9
  * and the checks alone. From the second round on, it first reviews the change
10
10
  * knowing nothing of the earlier reviews, then, in the same conversation,
11
11
  * checks what the previous one found against the code as it is now, and gives
12
- * its whole review again: nothing found earlier is lost on the way.
12
+ * its whole review again: nothing found earlier is lost on the way. A first
13
+ * answer that cannot be judged, such as a note sent while its sub-agents still
14
+ * ran, goes to the gate as it is: checked against the previous review, it
15
+ * would leave that review alone to repeat.
13
16
  */
14
17
  export declare function reviewer(profile: AgentProfile): Stage<"ok" | "modified">;
15
18
  /** An unmet criterion or a blocking finding sends the change back, unless the operator accepted the review. */
@@ -37,12 +37,25 @@ export const diff = step(async ({ dir, state, log, runDir }) => {
37
37
  log(`Large change: the review reads its diff from ${changes.diffFile}`);
38
38
  return { changes, reviewedTree };
39
39
  });
40
+ /** The review an agent's answer gives, or why it cannot be judged. */
41
+ function readReview(output, criteria) {
42
+ const parsed = reviewOutput.safeParse(output);
43
+ if (!parsed.success)
44
+ return {
45
+ problem: `Invalid structured output:\n${z.prettifyError(parsed.error)}`,
46
+ };
47
+ const problem = reviewProblem(parsed.data, criteria);
48
+ return problem ? { problem } : { review: parsed.data };
49
+ }
40
50
  /**
41
51
  * The reviewing agent: it judges the change against the spec, from the code
42
52
  * and the checks alone. From the second round on, it first reviews the change
43
53
  * knowing nothing of the earlier reviews, then, in the same conversation,
44
54
  * checks what the previous one found against the code as it is now, and gives
45
- * its whole review again: nothing found earlier is lost on the way.
55
+ * its whole review again: nothing found earlier is lost on the way. A first
56
+ * answer that cannot be judged, such as a note sent while its sub-agents still
57
+ * ran, goes to the gate as it is: checked against the previous review, it
58
+ * would leave that review alone to repeat.
46
59
  */
47
60
  export function reviewer(profile) {
48
61
  const call = (context) => agent({ profile, schema: reviewOutput, session: reviewSession, context });
@@ -57,28 +70,28 @@ export function reviewer(profile) {
57
70
  ...fresh,
58
71
  async run(context) {
59
72
  const first = await fresh.run(context);
60
- if ((facts(context.state).reviews ?? []).length === 0)
73
+ const { reviews, criteria } = facts(context.state);
74
+ if ((reviews ?? []).length === 0)
61
75
  return first;
76
+ if ("problem" in readReview(first.output, criteria)) {
77
+ context.log("The review's own answer cannot be judged: sent back before the previous review is checked");
78
+ return first;
79
+ }
62
80
  return recheck.run(context);
63
81
  },
64
82
  });
65
83
  }
66
84
  /** An unmet criterion or a blocking finding sends the change back, unless the operator accepted the review. */
67
85
  export const judgeReview = gate(["approved", "changes", "invalid"], async ({ state }) => {
68
- const parsed = reviewOutput.safeParse(state.output);
69
- if (!parsed.success)
70
- return {
71
- outcome: "invalid",
72
- reason: `Invalid structured output:\n${z.prettifyError(parsed.error)}`,
73
- };
74
- const problem = reviewProblem(parsed.data, facts(state).criteria);
75
- if (problem)
76
- return { outcome: "invalid", reason: problem };
77
- const changes = reviewChanges(parsed.data);
86
+ const read = readReview(state.output, facts(state).criteria);
87
+ if ("problem" in read)
88
+ return { outcome: "invalid", reason: read.problem };
89
+ const { review } = read;
90
+ const changes = reviewChanges(review);
78
91
  // Each review judged is kept, the last one apart for the stages after.
79
92
  const judged = {
80
- review: parsed.data,
81
- reviews: [...(facts(state).reviews ?? []), parsed.data],
93
+ review,
94
+ reviews: [...(facts(state).reviews ?? []), review],
82
95
  };
83
96
  // The operator resumed the run to deliver it as the review left it.
84
97
  if (changes && state.data.acceptedReview === true)
@@ -1 +1 @@
1
- {"version":3,"file":"review.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/review.ts"],"names":[],"mappings":"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"}
package/dist/cli/ui.js CHANGED
@@ -14,6 +14,8 @@ export async function ui(factory, port, loadError) {
14
14
  title: "factory",
15
15
  label,
16
16
  presentResult: (state) => presentResult(factory, state),
17
+ // Mounted under `/runs/`: the Kanban is one level up.
18
+ home: { label: "Board", href: "../" },
17
19
  });
18
20
  const page = await startFactoryUi({
19
21
  title: "factory",
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/cli/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAc,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,EAAE,CACtB,OAAgB,EAChB,IAAa,EACb,SAAkB;IAElB,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC;QAClC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,SAAS;QAChB,KAAK;QACL,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC;KACxD,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAC/B;QACE,KAAK,EAAE,SAAS;QAChB,KAAK;QACL,wFAAwF;QACxF,KAAK,EAAE,GAAmB,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC;QAC1D,IAAI;KACL,EACD,IAAI,CACL,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,eAAe,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,GAAG,qCAAqC,CAChF,CAAC;IACF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAC7B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACnB,OAAO,CAAC,CAAC;AACX,CAAC"}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/cli/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAc,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,EAAE,CACtB,OAAgB,EAChB,IAAa,EACb,SAAkB;IAElB,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC;QAClC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,SAAS;QAChB,KAAK;QACL,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC;QACvD,sDAAsD;QACtD,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE;KACtC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAC/B;QACE,KAAK,EAAE,SAAS;QAChB,KAAK;QACL,wFAAwF;QACxF,KAAK,EAAE,GAAmB,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC;QAC1D,IAAI;KACL,EACD,IAAI,CACL,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,eAAe,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,GAAG,qCAAqC,CAChF,CAAC;IACF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAC7B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACnB,OAAO,CAAC,CAAC;AACX,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clemrig/factory",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Take a repository's issues from spec to pull request with agents, watch them on a local Kanban, and run the repository's own workflows",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@clemrig/factory-ui": "0.2.0",
43
- "@clemrig/flow": "0.7.0",
44
- "@clemrig/flow-ui": "0.5.0"
43
+ "@clemrig/flow": "0.7.1",
44
+ "@clemrig/flow-ui": "0.6.0"
45
45
  }
46
46
  }
@@ -25,9 +25,21 @@ First find why the change let each defect happen. When defects come from the sam
25
25
  machine, the order locks are taken in or the way texts are built, or when a fix would add a special case for one
26
26
  situation, change the mechanism instead, reworking that part of your change if needed: prefer the fix that leaves
27
27
  less code and fewer special cases, a guard per case is the sign of a wrong mechanism. Fix each defect wherever the
28
- change has it, not only where the feedback points, then test each place. The review
29
- may add findings that do not block: fix them too when that is safe and stays within the spec, and say in your
30
- summary which you left and why. Then answer again.
28
+ change has it, not only where the feedback points.
29
+
30
+ Fix each unmet criterion and blocking finding of the review test first, at each place it occurs. Before changing
31
+ the code, write a test that reproduces its trigger as the review describes it, run it and see it fail: a test that
32
+ passes before the fix does not reproduce the defect yet, so make it reproduce that trigger rather than conclude the
33
+ defect is absent. Then fix it and see the test pass. When the review says a fix already holds and only its test is
34
+ missing, that test passes at once: check instead that it fails once you undo the fix, then put the fix back. Then
35
+ add a test for each situation around that one which your fix changes, such as another error, size, input mode or
36
+ state: a fix tested on its own case alone has broken the situation next to it. What no test in this repository can
37
+ show, such as a layout, a contrast or the browser's handling of a touch, reproduce in the browser before the fix
38
+ and check there after it. Change what an existing test expects only where the spec asks for the new behavior, and
39
+ say in your summary which tests you changed and why: a test realigned on what the code now does hides the
40
+ regression it should catch. The review checks each fix through these tests. It may add findings that do not block:
41
+ fix them too when that is safe and stays within the spec, and say in your summary which you left and why. Then
42
+ answer again.
31
43
 
32
44
  Answer with:
33
45
 
package/prompts/review.md CHANGED
@@ -39,13 +39,13 @@ answer. Never modify, stage, commit or delete anything in the repository: the ru
39
39
  your review, restores what changed and starts the review again.
40
40
 
41
41
  When the change is large (`large` is true), or spans several applications or layers, and you can launch
42
- sub-agents, split the whole review between them, five at most, and review no part yourself: a part you keep while
43
- you coordinate the others gets less attention than theirs. Each takes one part of the change, such as an
44
- application, a layer or a feature, with its checks in a browser when it shows in a web interface; one more, always,
45
- takes the paths that cross the parts: failures, interruptions, concurrent requests and recoveries. Give each the
46
- spec, its part, the files concerned and where to read the diff, and tell it to report its findings with their
47
- evidence, established as above, to look for each defect's other occurrences, to change nothing and to launch no
48
- agent itself. Check their reports, then give one answer for the whole change.
42
+ sub-agents, split the whole review between them for your first answer, five at most, and review no part yourself:
43
+ a part you keep while you coordinate the others gets less attention than theirs. Each takes one part of the
44
+ change, such as an application, a layer or a feature, with its checks in a browser when it shows in a web
45
+ interface; one more, always, takes the paths that cross the parts: failures, interruptions, concurrent requests and
46
+ recoveries. Give each the spec, its part, the files concerned and where to read the diff, and tell it to report its
47
+ findings with their evidence, established as above, to look for each defect's other occurrences, to change nothing
48
+ and to launch no agent itself. Check their reports, then give one answer for the whole change.
49
49
 
50
50
  Answer with:
51
51
 
@@ -67,15 +67,25 @@ Answer with:
67
67
  ## The previous review
68
68
 
69
69
  From the second round on, once you have answered, a request in this conversation gives `previous`, the review of
70
- the change's previous version. Check each unmet criterion and finding it gave against the code as it is now: when
71
- the change fixes it, put it in `resolved`, with its `summary` and the `evidence` of the fix; when it still holds,
72
- keep it in `findings`, with the same nature unless the code shows that nature was wrong, which its summary then
73
- says. Look at how each was fixed: a fix that adds a special case where the mechanism should change, or that makes
74
- the change harder to follow without need, is a finding, `maintainability`, or the defect's own nature when the
75
- mechanism still fails elsewhere. Then try each fix in the situations around the one its finding described: the
76
- other sizes, input modes and states of what the fix changed, such as a narrow screen, a touch, a keyboard, an empty
77
- or long value, a failure. A fix that breaks something that worked in the previous version is a `regression`. Then
78
- answer your whole review again: every criterion, and your findings with the previous ones that still hold, each
79
- once.
70
+ the change's previous version. Check it yourself, without launching sub-agents: your first answer has just
71
+ reviewed the whole change, and the implementing agent fixes each unmet criterion and blocking finding test first.
72
+ Do not check again what your first answer found: the code has not changed since.
73
+
74
+ For each unmet criterion and finding `previous` gave, read its fix and the tests that cover it, then run those
75
+ tests. A test proves a fix when it reproduces the trigger the finding described, would fail without the fix, and
76
+ covers the situations around it that the fix changed, such as another error, size, input mode or state. Read also
77
+ the tests the fix changed: an assertion relaxed or realigned on what the code now does, where the spec does not ask
78
+ for it, hides a regression. Check in the browser only what no test can show, such as a layout, a contrast or the
79
+ browser's handling of a touch.
80
+
81
+ When the fix is proven, put the finding in `resolved`, with its `summary` and the `evidence` of the fix. A fix of a
82
+ behavior that no test proves, nor a browser check where no test can show it, is not resolved: keep its finding with
83
+ its nature, and say in its summary which test is missing and whether the fix itself holds, as far as you
84
+ established it. When it still holds, keep it in `findings`, with the same
85
+ nature unless the code shows that nature was wrong, which its summary then says. Look at how each was fixed: a fix
86
+ that adds a special case where the mechanism should change, or that makes the change harder to follow without
87
+ need, is a finding, `maintainability`, or the defect's own nature when the mechanism still fails elsewhere. A fix
88
+ that breaks something that worked in the previous version is a `regression`. Then answer your whole review again:
89
+ every criterion, and your findings with the previous ones that still hold, each once.
80
90
 
81
91
  The change is approved only when every criterion is met and no finding is blocking.