Skip to content

Commit 78772c3

Browse files
committed
feat: update agent-proposed requirements
1 parent 78f7c88 commit 78772c3

18 files changed

Lines changed: 443 additions & 10 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,15 @@ flowchart LR
6969

7070
1. A human creates a Requirement and chooses Codex or Claude Code, optionally pinning a model and reasoning effort. Code Factory creates a dedicated, persistent RD session for it.
7171
2. Agent Manager starts or resumes that agent in the managed workspace. Messages sent during a run are queued; the human may explicitly interrupt when an immediate correction is needed.
72-
3. The RD agent edits and tests the repository, then uses the bundled `code-factory-cli` to register any pull request it creates, propose separate follow-up work (optionally starting it immediately), inspect its direct parent and child Requirements, message their RD Agents, or schedule a wake-up while a long external build or command continues. Related-Agent messages are persisted in the target Requirement conversation and start or queue its long-lived RD session.
72+
3. The RD agent edits and tests the repository, then uses the bundled `code-factory-cli` to register any pull request it creates, propose separate follow-up work (optionally starting it immediately), update its still-TODO proposals, inspect its direct parent and child Requirements, message their RD Agents, or schedule a wake-up while a long external build or command continues. Related-Agent messages are persisted in the target Requirement conversation and start or queue its long-lived RD session.
7373
4. Agent Triggers route normalized, deduplicated messages into the Requirement conversation. The timer trigger executes configurable one-time or recurring timers and sends their ID and follow-up description when due. The GitHub triggers poll registered PRs whose last stored state is Draft or Open, observing state, comments and reviews, CI failures, and merge conflicts. This lets the reconciler discover when a Draft PR becomes Open. Once a PR transitions to Closed or Merged, it is excluded from later polls. An idle RD session resumes immediately; a running session consumes the new messages after its current run.
7474
5. A human can request a short-lived AI review for an open PR with its own provider, model, and reasoning effort. Review results return to the same conversation and wake the original RD session to continue the loop.
7575

7676
The Agent Trigger boundary is intentionally source-neutral, but its current message contract targets an existing Requirement. Scheduled wake-ups are natively configurable; dynamic discovery and configuration of third-party trigger implementations, Slack and Jira sources, and triggers that create new Requirements are future extensions rather than implemented behavior.
7777

7878
Different Requirements can run concurrently, while each Requirement has at most one active RD run. Requirement state, conversations, runs, sessions, PR metadata, scheduled wake-ups, and Agent Trigger receipts are persisted in SQLite. Cancelled Requirements are retained for 7 days and completed Requirements for 365 days by default; both periods are runtime-configurable, and expiry atomically removes the Requirement and its related domain records while retaining retryable tombstones until attachment files are deleted.
7979

80-
Agent Manager places `code-factory-cli` on every RD process's `PATH` and injects its API URL, Requirement ID, and Session ID through the environment. The RD prompt describes available capabilities and leaves command names and arguments to `code-factory-cli --help`; raw HTTP details remain an internal transport contract. Use `code-factory-cli pr register --from-github <PR-URL>` to read current metadata through `gh`, and `requirement propose --description-file <PATH> [--start]` for multiline follow-up descriptions with optional immediate execution. Resumed prompts retain the current Requirement context and instruct agents to check for already-completed actions before retrying.
80+
Agent Manager places `code-factory-cli` on every RD process's `PATH` and injects its API URL, Requirement ID, and Session ID through the environment. The RD prompt describes available capabilities and leaves command names and arguments to `code-factory-cli --help`; raw HTTP details remain an internal transport contract. Use `code-factory-cli pr register --from-github <PR-URL>` to read current metadata through `gh`, `requirement propose --description-file <PATH> [--start]` for multiline follow-up descriptions with optional immediate execution, and `requirement update --requirement-id <ID>` to correct a proposal before it starts. Resumed prompts retain the current Requirement context and instruct agents to check for already-completed actions before retrying.
8181

8282
Agent context is scoped to the Requirement rather than copied from whichever interactive agent or terminal started Agent Manager. A Requirement's first RD Run creates a new native Codex thread or Claude Code session; later Runs resume that same native session, preserving its conversation context. Because the child CLI inherits Agent Manager's environment and starts in the managed workspace, it also discovers the provider's configured project/user instructions, Skills, plugins, and local memory features according to the CLI's own rules. Code Factory does not automatically copy another agent's live transcript or merge context from other Requirements. An explicit related-Agent message is new, durable input to the target Requirement rather than shared session context. Code Factory does not guarantee that every provider-managed memory entry is injected.
8383

apps/web/lib/agent-manager-client.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ import { MANAGER_EVENT_TYPES } from './manager-event-types.ts';
55

66
void test('subscribes to live Agent trace events', () => {
77
assert.ok(MANAGER_EVENT_TYPES.includes('run.trace.appended'));
8+
assert.ok(MANAGER_EVENT_TYPES.includes('requirement.updated'));
89
});

apps/web/lib/dashboard-state.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ void test('does not apply an in-flight scoped response after its Requirement is
192192
});
193193

194194
void test('routes legacy SSE payloads to precise resource refreshes', () => {
195+
assert.deepEqual(refreshTargetsForManagerEvent(managerEvent('requirement.updated')), [
196+
{ scope: 'requirement', requirementId: 'requirement-1', includeRuns: false },
197+
]);
195198
assert.deepEqual(refreshTargetsForManagerEvent(managerEvent('message.created')), [
196199
{ scope: 'messages', requirementId: 'requirement-1' },
197200
{ scope: 'requirement', requirementId: 'requirement-1', includeRuns: false },
@@ -221,6 +224,7 @@ void test('routes legacy SSE payloads to precise resource refreshes', () => {
221224
void test('does not refresh when a common SSE event carries persisted resources', () => {
222225
const payloads: Array<[string, Record<string, unknown>]> = [
223226
['requirement.created', { requirement: { id: 'requirement-1' } }],
227+
['requirement.updated', { requirement: { id: 'requirement-1' } }],
224228
['message.created', { message: { id: 'message-1', requirementId: 'requirement-1' }, requirement: { id: 'requirement-1' } }],
225229
['run.started', { run: { id: 'run-1', requirementId: 'requirement-1' }, requirement: { id: 'requirement-1' } }],
226230
['run.succeeded', { run: { id: 'run-1', requirementId: 'requirement-1' }, requirement: { id: 'requirement-1' } }],

apps/web/lib/dashboard-state.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ export function refreshTargetsForManagerEvent(event: ManagerEventDto): Dashboard
183183
const hasTimer = Boolean(requirementId && event.payload.timer?.requirementId === requirementId);
184184
switch (event.type) {
185185
case 'requirement.created':
186+
case 'requirement.updated':
186187
return !hasRequirement && requirementId
187188
? [{ scope: 'requirement', requirementId, includeRuns: false }]
188189
: [];
@@ -298,6 +299,7 @@ export function mergeRefreshTargets(
298299
export function managerEventInvalidatesSearch(event: ManagerEventDto): boolean {
299300
return [
300301
'requirement.created',
302+
'requirement.updated',
301303
'requirement.deleted',
302304
'requirements.purged',
303305
'message.created',

apps/web/lib/manager-event-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export const MANAGER_EVENT_TYPES = [
22
'requirement.created',
3+
'requirement.updated',
34
'requirement.deleted',
45
'requirement.completed',
56
'requirements.purged',

docs/agent-manager-api.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ The service listens only on the loopback interface by default and currently has
6666
| GET | /api/events | Subscribe to the resumable SSE stream |
6767
| POST | /api/agent/pull-requests | Register or update a PR from an RD Agent |
6868
| POST | /api/agent/requirements | Propose a follow-up Requirement from an RD Agent |
69+
| PATCH | /api/agent/requirements/:id | Update a TODO Requirement proposed by the current RD Agent |
6970
| GET | /api/agent/requirements/:id/related | List a source Requirement's direct parent and children |
7071
| POST | /api/agent/requirements/:id/related/:targetId/messages | Message a directly related Requirement's RD Agent |
7172

@@ -703,6 +704,7 @@ These endpoints are the transport used by `code-factory-cli` and other trusted l
703704
~~~bash
704705
code-factory-cli pr register --help
705706
code-factory-cli requirement propose --help
707+
code-factory-cli requirement update --help
706708
code-factory-cli requirement related --help
707709
code-factory-cli requirement message --help
708710
code-factory-cli timer register --help
@@ -782,6 +784,40 @@ curl -X POST http://127.0.0.1:4310/api/agent/requirements \
782784

783785
Success: 201 Created with the new Requirement and `createdBy=rd_agent`. Without `start: true`, it remains TODO. When `start` is true, the response reflects its started RD Session. Returns 404 for an unknown source Session and 400 when `parentRequirementId` does not match or another field is invalid.
784786

787+
### PATCH /api/agent/requirements/:targetRequirementId
788+
789+
Updates the title and/or description of a follow-up Requirement before it starts. The target must be a TODO child proposed by the same source RD Session. This prevents an Agent from changing unrelated, human-created, running, or terminal work.
790+
791+
Request body:
792+
793+
| Field | Type | Required | Meaning |
794+
| --- | --- | --- | --- |
795+
| sourceRequirementId | string | yes | Current Requirement injected into the source RD Agent |
796+
| sourceSessionId | string | yes | Current Requirement's RD Session |
797+
| title | string | no | Replacement title |
798+
| description | string | no | Replacement description |
799+
800+
At least one of `title` or `description` is required. Omitted fields retain their current values.
801+
802+
~~~bash
803+
curl -X PATCH http://127.0.0.1:4310/api/agent/requirements/req_child \
804+
-H 'Content-Type: application/json' \
805+
-d '{
806+
"sourceRequirementId": "req_parent",
807+
"sourceSessionId": "ses_parent",
808+
"description": "Corrected follow-up scope"
809+
}'
810+
~~~
811+
812+
Success: 200 OK with the updated Requirement and Session. Returns 404 for an unknown source or target, 400 for invalid input or a mismatched source Session, and 409 when the target was not proposed by the source or is no longer TODO.
813+
814+
The CLI supplies the source identifiers from its injected context. Use `requirement related` to discover child IDs:
815+
816+
~~~bash
817+
code-factory-cli requirement update --requirement-id req_child --title 'Corrected title'
818+
code-factory-cli requirement update --requirement-id req_child --description-file ./corrected-scope.md
819+
~~~
820+
785821
### GET /api/agent/requirements/:sourceRequirementId/related
786822

787823
Returns the source Requirement's direct parent and children as `{ "parent": Requirement | null, "children": Requirement[] }`, including terminal records that have not yet expired. The required `sourceSessionId` query parameter must identify the source Requirement's RD Session.
@@ -843,6 +879,7 @@ Current event types and primary payloads:
843879
| Event | Payload |
844880
| --- | --- |
845881
| requirement.created | requirement, provider, createdBy |
882+
| requirement.updated | updated requirement |
846883
| requirement.deleted | terminal requirement and its timers |
847884
| requirement.completed | updated requirement and its timers |
848885
| requirements.purged | requirementIds, cancelledCount, doneCount |

docs/agent-runners.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,25 @@ RD Agents use self-describing commands instead of constructing Agent API request
4141
```bash
4242
code-factory-cli pr register --help
4343
code-factory-cli requirement propose --help
44+
code-factory-cli requirement update --help
4445
code-factory-cli timer register --help
4546
code-factory-cli timer show --help
4647
code-factory-cli timer cancel --help
4748
```
4849

49-
`pr register` registers a newly created PR or refreshes metadata changed by the RD Agent. `requirement propose` records separate follow-up work as a linked TODO Requirement. `requirement related` lists direct parent and child Requirements; `requirement message` coordinates with their RD Agents. `timer register` registers a one-time wake-up by default or a recurring one with `--repeat`; `timer show` recovers timer IDs and statuses for the current Requirement; `timer cancel` stops an active timer. The commands print the Agent API JSON response on stdout. Exit code `0` means success, `2` means invalid input or missing context, and `1` means an execution, network, HTTP, or response-format failure. Errors go to stderr. API requests and GitHub lookups time out after 30 seconds; writes are never automatically retried. A timeout or invalid response can occur after the server commits a write: inspect the Requirement before retrying, especially when proposing follow-up work.
50+
`pr register` registers a newly created PR or refreshes metadata changed by the RD Agent. `requirement propose` records separate follow-up work as a linked TODO Requirement, and `requirement update` corrects the title or description of a proposal before it starts. `requirement related` lists direct parent and child Requirements; `requirement message` coordinates with their RD Agents. `timer register` registers a one-time wake-up by default or a recurring one with `--repeat`; `timer show` recovers timer IDs and statuses for the current Requirement; `timer cancel` stops an active timer. The commands print the Agent API JSON response on stdout. Exit code `0` means success, `2` means invalid input or missing context, and `1` means an execution, network, HTTP, or response-format failure. Errors go to stderr. API requests and GitHub lookups time out after 30 seconds; writes are never automatically retried. A timeout or invalid response can occur after the server commits a write: inspect the Requirement before retrying, especially when proposing or updating follow-up work.
5051

5152
Prefer registration from an explicit PR URL, using the authenticated local `gh` CLI:
5253

5354
```bash
5455
code-factory-cli pr register --from-github https://github.com/OWNER/REPO/pull/123
5556
code-factory-cli requirement propose --title 'Follow-up task' --description-file ./follow-up.md --start
57+
code-factory-cli requirement update --requirement-id req_... --description-file ./corrected-follow-up.md
5658
```
5759

5860
`--from-github` reads the PR number, title, URL, branches, head SHA, and state from GitHub and validates the returned identity before registration. The repository key comes from the returned URL and is normalized to lowercase, matching Manager and Store identity checks. GitHub Enterprise URLs are supported and retain the hostname in the repository identifier. It cannot be mixed with manual metadata flags. The existing full manual registration form remains supported for callers that already have a snapshot; an existing PR's lifecycle is still owned by the reconciler, even when `--status` is supplied. The CLI does not create or edit GitHub PRs.
5961

60-
`--description-file` reads a UTF-8 file relative to the CLI's working directory and is mutually exclusive with `--description`. This avoids shell quoting problems for multiline descriptions. Proposed Requirements remain TODO by default; `--start` starts the new Requirement's RD Session immediately.
62+
`--description-file` reads a UTF-8 file relative to the CLI's working directory and is mutually exclusive with `--description`. This avoids shell quoting problems for multiline descriptions. Proposed Requirements remain TODO by default; `--start` starts the new Requirement's RD Session immediately. Only the same source RD Session can update its directly proposed child, and only while that child remains TODO.
6163

6264
The RD behavioral prompt is supplied on both initial and resumed invocations. It describes CLI capabilities and behavioral constraints, leaving command names and arguments to `code-factory-cli --help`. It covers Requirement scope, worktree isolation, recovery without duplicate actions, control-plane commands, lifecycle ownership, evaluating external feedback, and evidence-based handoff. Task-specific content stays in the stdin prompt; every invocation includes the current Requirement ID, title, and description, with only new external conversation messages on resume. Questions and investigations do not inherently require code changes, and the RD Agent must not substitute follow-up proposals for work required by the current Requirement. A successful GitHub PR creation and a successful Code Factory registration are separate outcomes; a failed registration must be reported without recreating the PR. Human confirmation owns Requirement completion.
6365

docs/architecture.en.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ Every headless RD and Reviewer invocation skips interactive approval and CLI san
8080
- optional `model` and `reasoningEffort` pin the CLI configuration for every RD Run in the Session;
8181
- `createdBy`: `human | rd_agent`;
8282
- an agent-proposed Requirement records `parentRequirementId` and `sourceSessionId`;
83+
- the source RD Session may update that proposed child's title or description only while it remains TODO;
8384
- an agent proposal is created as TODO by default, preventing uncontrolled recursive work; an explicit CLI `--start` opt-in starts its RD Session immediately.
8485
- the proposing Requirement and its direct child can discover each other and exchange explicit, durable RD messages without sharing native agent-session context.
8586

docs/protocol.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,22 @@ Content-Type: application/json
121121

122122
`provider` is optional and defaults to the source Session provider. The proposed Requirement is created as `createdBy=rd_agent`. It remains TODO by default; set the optional boolean `start` to `true` (or pass `--start` to `code-factory-cli requirement propose`) to start its RD Session immediately.
123123

124+
Update a still-TODO Requirement proposed by the current RD Session:
125+
126+
~~~http
127+
PATCH /api/agent/requirements/req_child
128+
Content-Type: application/json
129+
130+
{
131+
"sourceRequirementId": "req_parent",
132+
"sourceSessionId": "ses_parent",
133+
"title": "Corrected follow-up title",
134+
"description": "Corrected follow-up scope"
135+
}
136+
~~~
137+
138+
`title` and `description` are partial updates, with at least one required. The target must be a direct child created by the same source RD Session and must still be TODO. `code-factory-cli requirement update --requirement-id req_child` supplies the source identifiers from the injected context.
139+
124140
Inspect and message directly related Requirements:
125141

126142
~~~bash
@@ -164,7 +180,7 @@ data: {"id":42,"type":"review_request.started",...}
164180

165181
Current event types include:
166182

167-
- `requirement.created` / `requirement.deleted` / `requirement.completed` / `requirements.purged`;
183+
- `requirement.created` / `requirement.updated` / `requirement.deleted` / `requirement.completed` / `requirements.purged`;
168184
- `message.created`;
169185
- `pull_request.created` / `pull_request.updated`;
170186
- `review_request.started`;

0 commit comments

Comments
 (0)