engineering-memory 1.11.9 → 1.11.11
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/dispatcher/sections.mjs +7 -2
- package/install/codex-approval.mjs +457 -0
- package/install/files.mjs +5 -1
- package/install/installer.mjs +24 -1
- package/package.json +1 -1
- package/runtime/build.json +1 -1
- package/runtime/dist/src/config.js +2 -0
- package/runtime/dist/src/git/git-inspector.js +114 -4
- package/runtime/dist/src/git/verification-gate.js +30 -17
- package/runtime/dist/src/mcp/onboarding-tools.js +110 -16
- package/runtime/dist/src/mcp/questionnaire-tools.js +99 -15
- package/runtime/dist/src/mcp/tool-annotations.js +112 -0
- package/runtime/dist/src/mcp/tool-definitions.js +150 -29
- package/runtime/dist/src/mcp/worktree-tools.js +395 -236
- package/runtime/dist/src/project/repository.js +26 -8
- package/runtime/dist/src/runtime/active-context-store.js +3 -0
- package/runtime/dist/src/runtime/api-client.js +1 -0
- package/runtime/dist/src/runtime/branch-preferences.js +0 -19
- package/runtime/dist/src/runtime/bridge-service.js +1320 -246
- package/runtime/dist/src/runtime/offline-outbox.js +33 -158
- package/runtime/dist/src/runtime/phase-timer.js +26 -0
- package/runtime/dist/src/runtime/privacy-detector.js +263 -0
- package/runtime/dist/src/runtime/questionnaire-store.js +210 -49
- package/runtime/dist/src/runtime/recovery-error.js +3 -1
- package/runtime/dist/src/runtime/runtime-entry.js +8 -0
- package/runtime/dist/src/runtime/runtime-host.js +1 -3
- package/runtime/dist/src/runtime/task-branch-store.js +17 -1
- package/runtime/dist/src/runtime/task-start.js +281 -0
- package/runtime/dist/src/runtime/worktree-pool.js +374 -26
- package/runtime/dist/src/utilities/process.js +1 -0
- package/skill/SKILL.md +3 -3
- package/skill/references/lifecycle.md +59 -19
- package/skill/references/memory-updates.md +9 -3
- package/skill/references/project-onboarding.md +19 -2
- package/skill/references/questionnaires.md +86 -15
|
@@ -6,13 +6,21 @@ Sign-in decides nothing beyond who the user is. The organization and the project
|
|
|
6
6
|
|
|
7
7
|
Use the binding returned by `session.entry` as the authority. The bridge stores the selected project and repository identity under `~/.engineering-memory/origins/<api-hash>/project-bindings/`, outside the installed runtime and working tree. It imports a legacy `.engineering-memory/project.json` once, preserving its schema and exact backend fingerprint for existing tasks and receipts. It leaves that file unchanged; after migration the file is optional and may be removed through the repository's normal Git workflow. New bindings never create it. A checkout that never imported the marker still finds a project bound under the older identity: the bridge presents that identity alongside the current one and stores whichever the backend confirms. Never infer a selection from a directory or remote, and never edit binding records by hand. Separate clones or a changed remote require a new explicit selection; worktrees of the same checkout share the local binding.
|
|
8
8
|
|
|
9
|
+
A project is not tied to one repository forever. When its code moves — to another host, under a new remote, into a history that starts again from one commit — the project moves with it and keeps everything: tasks, memory, work items, open sessions, the local state of every checkout. The old repository stays in the project's history. Recognise the three shapes this takes and never answer any of them by creating a second project:
|
|
10
|
+
|
|
11
|
+
- Binding the selected project is refused with recovery `project.move_repository`. The project lives in another repository and the user is one of its owners. Call `project.move_repository` with the project id; it opens one native form that names both repositories and what stays with the project, and on approval moves the project and binds this checkout. A declined form changes nothing.
|
|
12
|
+
- The same refusal arrives with recovery `project.member_list`. Moving needs project-owner authority, which the organization owner also holds. Check the roles, say that an owner has to run the move from a checkout of the new repository, and continue only work that does not need the binding. Do not name a person.
|
|
13
|
+
- `session.entry`, `project.resolve` or bootstrap reports `projectMoved`, or bootstrap refuses with "Project moved". This checkout still points at a repository the project has left; the message names where it lives now. Tasks already open here can be resumed and finished. New work starts from the new repository: the user points this checkout's origin at it, or clones it, and selects the project again. An owner calls `project.move_repository` from here only when the code truly lives in this repository again.
|
|
14
|
+
|
|
15
|
+
After a move nothing else changes for the agent. The binding keeps reporting the identity the project has always had, so resume, verification, receipts and the commit gate behave as they did before.
|
|
16
|
+
|
|
9
17
|
An archived project is recoverable state, not a missing or conflicting binding. When an owner receives `project.restore`, use the `projectId` and `expectedVersion` in its data and call that operation before retrying. A non-owner receives `project.member_list` instead: check the available roles and explain that project-owner authority is required to restore this exact project before retrying. Do not name a person or disclose contact details. Use `project.list` with `includeArchived: true` when an owner must first select the archived project. Never create or bind a replacement project to escape archive state.
|
|
10
18
|
|
|
11
|
-
For a bound repository, call `session.bootstrap` before producing a plan or changing files. Supply the current repository root, project ID, task ID or stable local task slug, objective, task kind, task mode, and current Git diff hash. Use `read_only` for review, diagnosis, planning, or reporting without write authority; use `scaffold` when the task applies organization architecture templates to a new project; use `write` when the request authorizes repository changes. The bridge records the read-only Git diff hash as the immutable task baseline, including a pre-existing dirty worktree. Use the returned task ID, task version, context session ID,
|
|
19
|
+
For a bound repository, call `session.bootstrap` before producing a plan or changing files. Supply the current repository root, project ID, task ID or stable local task slug, objective, task kind, task mode, and current Git diff hash. `externalTaskId`, `objective`, `taskKind` and `workItemKey` are each one line with a fixed maximum length (160/240/80/120 characters); the bridge refuses an over-length or multi-line value locally, in milliseconds, before any Git or backend work, and names the limit in the refusal — put detail that does not fit in checkpoints, not in the objective. Use `read_only` for review, diagnosis, planning, or reporting without write authority; use `scaffold` when the task applies organization architecture templates to a new project; use `write` when the request authorizes repository changes. The bridge records the read-only Git diff hash as the immutable task baseline, including a pre-existing dirty worktree. Use the returned task ID, task version, context session ID, project profile, engineering rules, prior task documents, quality gates, and current deviations; `deferredResources` names what did not fit, each with the `revisionId` to read it by.
|
|
12
20
|
|
|
13
|
-
Before a new write or scaffold task, read `session.entry` and the project Git preferences. If `canManage` is true,
|
|
21
|
+
Before a new write or scaffold task, read `session.entry` and the project Git preferences. If `canManage` is true, and any of development/production/test is still unanswered, ask them through one native form and save them with `project.set_git_preferences` — the tool asks every supplied role as its own question in that single form, then saves the approved roles together in one PUT. Development needs a branch; production/test may explicitly be absent. Their answered flags prevent repeated questions. Other members choose only a task-specific base and continue without changing shared preferences. A later request to change a base updates future tasks only. Branch preferences never label memory sources or change commit/tree applicability.
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
Start new write or scaffold work with `task.branch`, before `session.bootstrap`, passing the stable `externalTaskId` and `repoRoot`. It opens one native form: where the branch starts (the configured project branches, this folder's branch and commit, or another remote branch), the new branch's name with a suggested default, and the folder: a separate managed worktree, this folder as a new branch when it is clean (moving out an unfinished task that holds it, which that option names), this folder on its current branch, or not now. Pass `base` or `name` only when the user has already said them; those questions then drop out of the form. Show the whole form in as few native control calls as the host allows, relay every answer together, then call `task.branch` again with the same arguments, which is the `retry` the relay returns. That call allocates exactly what was answered, fetches a remote base and pins its commit, and returns the `repoRoot` to bootstrap in. When the fetch fails it asks once whether to continue from the local commit; never select a cached branch silently. "Not now" returns `status: 'deferred'` with nothing created, reserved or saved: say so in one line, continue only read-only work, and call the returned `reconsider` exactly as given once the user asks to start it. A refusal that names a new `decisionAttempt`, such as a branch name that already exists, asks the form again under that attempt. Existing task decisions survive retries and restarts; resume a recorded branch rather than recreating or resetting it.
|
|
16
24
|
|
|
17
25
|
Use the returned `repoRoot` for **every** file read/write, terminal, context, validation and Git/delivery operation. The user-local pool is shared by Codex and Claude. Managed directories live under `engineering_memory/worktrees/<stable-project-folder>/<folder>_worktreeN`, at the root of the system drive on Windows (`C:\engineering_memory\worktrees\...`) and in the home directory elsewhere, or under the absolute directory named by `worktree-root.json` in the user-level API state directory when that file exists; `worktree.list` reports the effective root, and worktrees created by earlier clients under the Documents folder keep working where they are. Do not supply arbitrary `worktreePath` values or create ad-hoc siblings. Independent clones cannot reuse each other's worktrees. `worktree.list` explains which slots are active, inactive but protected, or safely reusable. The versioned backend policy defaults to 50 directories per project/computer, 30-second heartbeats and 10-minute inactivity. Protected inactive directories still count toward the limit. Only a global admin changes `worktree.set_policy`; it is not an environment setting.
|
|
18
26
|
|
|
@@ -20,7 +28,7 @@ Renew `task.heartbeat` using the exact returned task, path and ownership generat
|
|
|
20
28
|
|
|
21
29
|
Use `task.pause` when work stops or is handed to another client. It preserves task state and files, invalidates the old owner, and allows safe clean directories to be reused. It is not `task.abandon`. Resume reacquires the original directory when possible or uses the retained task branch in another safe directory. `worktree.reconcile` recovers interrupted allocation and validated local ownership records against Git. Never edit the registry, force checkout, stash/reset/clean, remove branches, kill a client or delete a worktree to bypass a refusal.
|
|
22
30
|
|
|
23
|
-
`task.close` retains a managed checkout while changes or delivery remain pending.
|
|
31
|
+
`task.close` retains a managed checkout while changes or delivery remain pending. After delivery, release with `deliveryOutcome:'delivered'` and the current generation. When the user decides not to deliver, call `worktree.release` with `deliveryOutcome:'cancelled'`; the product asks what happens to the folder and saves any changes to a ref. Never run `git restore` or `git clean` yourself to free a worktree, and never report a delivery that did not happen. Dirty/staged/untracked files, pending outbox/intents and unfinished Git operations prevent release. An old release cannot affect a reused directory. User-owned existing directories are never added to automatic cleanup. When the user wants to work in the main folder, they answer that in `task.branch`'s start form; when an open task holds the folder, that option names the task that would move out. A closed or abandoned task stops holding the folder by itself; a task still running in another client keeps it until it is paused there. Never edit or delete the reservation ref by hand.
|
|
24
32
|
|
|
25
33
|
Read-only tasks allocate no worktree. If the user authorizes writing, settle `task.branch` for that same externalTaskId, use the allocated repoRoot, then call `context.prepare_change` with `transitionToWrite`. Its pinned source must still match; choosing a different source requires a new task, never rewriting the original task's source identity. Existing lease, discipline, source-memory, verify and commit gates remain mandatory.
|
|
26
34
|
|
|
@@ -30,7 +38,7 @@ A task nobody is going to finish is abandoned rather than inherited. Ask the use
|
|
|
30
38
|
|
|
31
39
|
If an existing task is identified or execution resumes after compaction, call `session.resume`. Reconcile backend sequence, local outbox, Markdown projections, current Git diff, pinned revisions, and the active lease before any further action.
|
|
32
40
|
|
|
33
|
-
Reconcile pending questions too. A required question remains unanswered across a timeout, dismissed form, interruption or restart. Use `questionnaire.resume` for its recorded identifier and wait for an explicit valid response before dependent work. Follow `questionnaires.md`; do not create a replacement async prompt or consume an unrelated task's answer.
|
|
41
|
+
Reconcile pending questions too. A required question remains unanswered across a timeout, dismissed form, interruption or restart. Use `questionnaire.resume` for its recorded identifier and wait for an explicit valid response before dependent work. Follow `questionnaires.md`; do not create a replacement async prompt or consume an unrelated task's answer. A question belongs to the task the chat was working on when it was asked. `pendingQuestionnaires` lists this chat's own questions and those that belong to no task in full; another task's appear under `otherTasksPendingQuestionnaires` as a one-line summary naming that task. Resume one of those only when this chat is working on the task it names.
|
|
34
42
|
|
|
35
43
|
## Discovery
|
|
36
44
|
|
|
@@ -85,6 +93,12 @@ Do not pull history for everything. Pull it for the records the task actually to
|
|
|
85
93
|
|
|
86
94
|
Send calls that do not feed each other in one batch rather than one at a time: reads of any kind, and proposals for different records. Proposal drafting, submission and pending approval do not block independent task work. Use host-authorized background agents or concurrent tools and continue the next independent unit; do not immediately wait for the worker. If concurrency is unavailable, checkpoint the pending draft and defer submission until an operation needs it. Follow `references/memory-updates.md` for ownership, delivery tracking and recovery. Reconcile every record the task touched in a single `task.reconcile` call with `entries`, not one call per record.
|
|
87
95
|
|
|
96
|
+
That is a rule for work that can run beside the task, not for the checks that steer it. A check whose answer decides the next action — delivery preconditions such as the commit hook, the remote and the branch; whether verification or close can pass; what a waiver allows — is run by the agent that takes that action, in its own turn. Hand work to another agent only when you have other real work to do while it runs, and never take the action a delegated check gates before its answer is back: a report that arrives after the commit it should have stopped is worth nothing. Work another agent checked is neither the user's approval nor task verification.
|
|
97
|
+
|
|
98
|
+
Size delegation to what the task actually needs: a one- or two-file change carries no discovery a single agent cannot finish itself, and a multi-agent workflow on it adds a wait without adding an answer. Once work is delegated, wait for its real completion signal — a task notification, a returned result — never a sleep loop polling its journal or log, and never a blocking wait on its output; keep doing the next independent piece of lifecycle work and collect the delegated result when it actually arrives.
|
|
99
|
+
|
|
100
|
+
Order blocking questions the same way: finish everything that does not depend on the answer — the models, the services, the parts of the implementation the answer cannot change — before opening one, so a user who is away comes back to progress instead of an untouched task. Ask at the point where the answer is actually needed, not at the start of discovery merely because that is where the question first came up.
|
|
101
|
+
|
|
88
102
|
Checkpoints, recorded corrections, reconciliations, self-reviews and scaffold application receipts return before the backend has them, reporting `deliveryStatus: 'pending'` with the task version they will occupy. The local recording call is complete; backend delivery remains pending and is already under way. Do not wait for it, poll it, or send it again before a dependent operation. Collect a proposal's identifiers when its approval or reconciliation needs them, and an approved revision before work that depends on its changed contract. A prepared lease, required synchronization and verification must succeed before the operations that require them; unrelated proposal work is never a prerequisite for continuing this task.
|
|
89
103
|
|
|
90
104
|
Temporary code written to reach or force a path — a pinned state, a fixed service response, a jump straight to the screen — is allowed and expected, carries the marker `ENGINEERING-MEMORY-TEMPORARY` immediately followed by a colon and its reason, and is removed before verification. `task.verify` refuses while any marker is in the tree and names every line, and the commit gate refuses while one is staged.
|
|
@@ -157,7 +171,7 @@ reopening the task and abandoning it.
|
|
|
157
171
|
|
|
158
172
|
Skip change preparation for a read-only task. Do not request an edit lease, send changed paths, record `pre_edit`, or reconcile code resources in that mode. If the user later authorizes a change, call `context.prepare_change` with `transitionToWrite: true`, the bridge-owned current task version, intended paths, and current baseline diff. Continue only after the bridge returns the updated write task and lease.
|
|
159
173
|
|
|
160
|
-
Keep the path set narrow. A lease covering most of the repository returns more knowledge than one response can carry, and the resources that do not fit come back as `deferredResources` instead of content.
|
|
174
|
+
Keep the path set narrow. A lease covering most of the repository returns more knowledge than one response can carry, and the resources that do not fit come back as `deferredResources` instead of content. Read a deferred record with `memory.read_revisions` and the `revisionId` its entry carries before touching the path it governs; skip the ones that govern nothing you are changing. Prefer several small leases over one wide one.
|
|
161
175
|
|
|
162
176
|
For a write task, list the precise relative paths likely to change and call `context.prepare_change`. It must return a change lease and read receipts for every matched required resource. Read the full returned screen, component, service, navigation, localization, storage, or state record before editing its matched path.
|
|
163
177
|
|
|
@@ -189,9 +203,15 @@ A project-specific correction is offered as task-only or permanent for this proj
|
|
|
189
203
|
|
|
190
204
|
Before validation, read the changed code back against the rules that govern it. Not from memory: reread the returned records for the paths that changed, including anything the context pack deferred, and read the diff as the next person to open the file would. The restraint document is the first thing to hold it against.
|
|
191
205
|
|
|
192
|
-
|
|
206
|
+
`context.prepare_change` returns `governingRules`: for each changed path, the rules its role in the project profile binds it to. Record `task.self_review` with one entry per changed file (deleted files excepted) that names every one of those rules, and for a file the profile maps to no role, the engineering rules you actually read it against. Each rule gets one outcome:
|
|
193
207
|
|
|
194
|
-
|
|
208
|
+
- `follows`: the file does what the rule says.
|
|
209
|
+
- `fixed`: it did not, and you changed it; record what was wrong and what you changed.
|
|
210
|
+
- `user_accepted_deviation`: it does not follow the rule and the user decided to keep it that way. Record the rule's `resourceKey` and the issue; `task.self_review` asks the user the rule-deviation question natively and records the deviation only on their approval. If they choose to change the code, change it and record the rule as `fixed`.
|
|
211
|
+
|
|
212
|
+
There is no fourth outcome. Keeping code because the surrounding code already does it, or because the rule seemed not to fit, is a deviation the user has not approved, and verification refuses it. A rule conflict is a question for the user, and the right moment to ask it is before the code is written; existing code is evidence of what was done, not authority for what to do.
|
|
213
|
+
|
|
214
|
+
An area the task worked in that the coverage map marks as having no rule is named in the handoff, with what was decided in its absence, and proposed as a rule when a real task had to improvise there. That is how the product learns which rule to write next: an area nothing has needed yet can wait, and one a real task just had to improvise in cannot.
|
|
195
215
|
|
|
196
216
|
This applies to a write task. A read-only task changed nothing, so there is nothing to read back against the rules and `task.self_review` is refused for it; go straight to verification. `task.verify` refuses a write task without a self review of the current diff. Editing after the review invalidates it, which is the point: the last thing that happens to the code is that someone read it against the rules. This exists because a task once shipped code that broke rules it had been given — the rules were present and correct, and nothing in the lifecycle ever asked whether the result matched them.
|
|
197
217
|
|
|
@@ -201,19 +221,25 @@ Before validation begins, declare the discretion the task used. Send it as `deci
|
|
|
201
221
|
|
|
202
222
|
Record `validation_before`, run proportionate format, generation, static analysis, tests, security scans, and design checks, then record `validation_after` with command results and hashes. Each `task.verify` validation entry must contain the backend validation ID, the exact command, a successful result, and the SHA-256 hash of its sanitized output. Never invent a validation result or reuse a placeholder hash.
|
|
203
223
|
|
|
204
|
-
|
|
224
|
+
Run each required validation once, after the last edit, capturing and hashing its output in the same command. A rerun that follows no further code change adds no new evidence — the hash already on file still stands. Run the tests for the changed unit; run the whole suite at most once, and only when the change actually touched shared infrastructure the whole suite exercises. Run a code generator in its plain incremental form, which already rebuilds only what changed; never narrow it with a filter such as `build_runner`'s `--build-filter`, which makes it crawl for far longer and adds no evidence. Bind every test, build and search command to a timeout. Resolve a pinned dependency's path from its lockfile before searching it, and never grep an entire package cache or the home directory.
|
|
225
|
+
|
|
226
|
+
Available validation IDs are `format`, `static_analysis`, `tests`, `build`, `codegen`, `localization`, `ui`, `network`, `structure`, `diff_check`, `privacy_scan`, `dependency_audit`, `deploy_smoke`, and `read_only_integrity`. Use the exact set in the `requirements` that `context.prepare_change` and `session.resume` return: the required and optional validations with the paths that call for each and the command shapes each accepts, plus the history reads, memory mappings, reconciliations and checkpoints verification will ask for. Produce all of them before `task.verify`, so what verification wants is known up front rather than discovered one refusal at a time. A command is matched by the tool it runs, so a full path to `dart.exe`, `flutter.bat` or `flutter_tools.snapshot`, PowerShell's `&` and quotes all read as that tool. `memory.history` with `required: true` reads every record verification will ask about in one call; a path no record covers comes back in `uncoveredPaths` with the kind verification will want a record of. A handwritten-code task cannot substitute a generic command for analysis or tests. Dependency manifests, lockfiles, build configuration, and deployment configuration require their additional audit or smoke evidence. Read-only tasks require repository-integrity and privacy evidence without a change lease.
|
|
227
|
+
|
|
228
|
+
`ui` evidence must render or exercise the changed widget or screen: a widget, golden or screenshot test of that surface. A command that only matches the pattern without exercising the change is not UI evidence. When the user decides the UI check is not needed for this change, pass `waivers` to `task.verify` with their reason; the tool asks them in a native form and records the answer on the task. Never infer a waiver from prose, and never waive tests, static analysis or format — only `ui` can be waived. `task.close` and `session.resume` return the recorded waivers; name each one and its reason in the delivery summary.
|
|
205
229
|
|
|
206
230
|
For each changed screen or component, call `task.reconcile` with an approved proposal ID or a reasoned `no_semantic_memory_change` result. In a scaffold task, reconcile each applied template resource with a reasoned `scaffold_applied` result instead; files that still match their recorded scaffold hash need no per-file proposal, and every other changed path follows the normal rules. New screens and components require a memory proposal. Changed project contracts, service contracts, Figma mappings, or engineering rules require the corresponding proposal when their semantics changed.
|
|
207
231
|
|
|
208
|
-
|
|
232
|
+
When `context.prepare_change`'s `requirements` list a path under `mappings`, draft that proposal as soon as the lease is prepared — in the background if the host allows it, otherwise as the very next independent step — so its approval runs alongside validation instead of opening for the first time inside the verify loop.
|
|
233
|
+
|
|
234
|
+
Record `handoff_before`, then call `task.verify`. For a write task, verify the exact changed paths, current diff hash, validations, session, and lease. For a read-only task, send no changed paths, lease, or write baseline; the bridge supplies the current Git diff hash and the backend compares it with the baseline captured at bootstrap while also verifying bootstrap, discovery, validation-before, validation-after, handoff, pinned read receipts, and synchronized outbox evidence. If verification fails, the refusal lists every unmet requirement at once; resolve all of them, then verify again. When `session.resume` reports `context_refresh_required`, call `context.refresh`, then `context.prepare_change` for the same paths; resume alone never reactivates a stale session, and the task baseline does not change. `context.refresh` sends in full only the revisions that changed since the session pinned them and lists the rest in `unchangedResources`; reread one of those with `memory.read_revisions` only when its text is no longer in view. Do not state that the task is complete while verification is failing, and do not carry on writing code with the failure unaddressed — a task that never verifies never closes, and everything that depends on closing, including the commit gate, silently never happens.
|
|
209
235
|
|
|
210
236
|
When a task adds a structure the system did not have — a cache, a broker, a read replica, a second deployable, a projection, an event store — name it in `introducedStructures` at verification. The backend refuses any the project profile has not recorded under `architecture.adopted`, with the pressure it relieves, and refuses with the stated reason any the project recorded as deliberately declined. Recording it is a project profile revision like any other: propose, have the user approve, reconcile. Do not reach for the structure first and record it afterwards — the point of the record is that somebody decided.
|
|
211
237
|
|
|
212
238
|
A new screen or component fails verification until its memory exists, which takes four steps in order: `memory.propose_revision` for each new path, the user's explicit approval, `memory.review_proposal`, then `task.reconcile`. The error names the paths. Walk the chain rather than retrying the same verification.
|
|
213
239
|
|
|
214
|
-
Call `task.close` only after verification and only when the current diff still matches.
|
|
240
|
+
Call `task.close` only after verification and only when the current diff still matches. Where the Engineering Memory pre-commit hook was installed, it performs a fresh authenticated commit-gate check against the closed task, current membership, repository fingerprint, diff hash, and knowledge revisions. Without that hook nothing checks the commit; never say a hook confirmed the closure. A local receipt alone never authorizes commit. Closing a task does not authorize commit, push, publish, deploy, tag, or merge.
|
|
215
241
|
|
|
216
|
-
Closing is not the end of the turn either. `task.close` returns the delivery question
|
|
242
|
+
Closing is not the end of the turn either. `task.close` returns the delivery question — commit, commit and push, commit and push with a draft pull request, or commit and push with a pull request — and names the branch, remote and URL the work would go to. It is settled every time — by the user's own message when that already chose one, otherwise by asking. The two pull request options take the base branch from the user. Do exactly the one chosen and nothing more. When the host has to approve the push, follow the Delivery section of `questionnaires.md`; an Engineering Memory form is never the way past a host's denial.
|
|
217
243
|
|
|
218
244
|
When a pull request has been opened, keep going: check whether it merges cleanly, report the result with the link, and if it conflicts, name the files and ask whether to resolve them. Never end the turn on a pull request whose mergeability was never checked, and never resolve a conflict without being told to.
|
|
219
245
|
|
|
@@ -238,8 +264,13 @@ current. Select linkedSources explicitly by accessible project id, commit and tr
|
|
|
238
264
|
do not guess the counterpart's source from this repository's branch. Unselected or unknown linked
|
|
239
265
|
sources are not verified endpoint contracts.
|
|
240
266
|
|
|
241
|
-
|
|
242
|
-
|
|
267
|
+
If task.close reported sourcePublication.applicable, call memory.publish_task after the
|
|
268
|
+
already-authorized commit is created, with the closed task id and repoRoot; otherwise there is
|
|
269
|
+
nothing to publish, for one of two distinct reasons publish_task's reason field names: a project
|
|
270
|
+
with no inspected source snapshot at all has legacy-unverified coverage, while a source-aware task
|
|
271
|
+
whose commit and tree were never resolved to an inspected snapshot has unknown coverage.
|
|
272
|
+
publish_task itself reports {published:false, reason} rather than refusing in either case. The bridge
|
|
273
|
+
proves the resulting commit has the task's base parent and exactly its
|
|
243
274
|
verified files, blobs, modes and deletions. Backend publication rechecks the task, current access
|
|
244
275
|
and immutable source identity and atomically saves the approved overlay for the resulting commit.
|
|
245
276
|
It never publishes dirty worktree contents. Repeating the same result is idempotent. If merging,
|
|
@@ -257,12 +288,14 @@ An unexpired cached context pack may be used for implementation. The bridge reco
|
|
|
257
288
|
|
|
258
289
|
## Reuse before allocating another directory
|
|
259
290
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
291
|
+
`task.branch` reconciles managed Git checkouts with local records and chooses the smallest safe
|
|
292
|
+
slot itself, so do not call `worktree.list` before it, guess a new worktree number or create a
|
|
293
|
+
directory yourself. Use `worktree.list` when the pool is full or the user asks about old folders:
|
|
294
|
+
it shows which checkouts are protected and why. Old unregistered managed checkouts count toward the limit and remain
|
|
264
295
|
protected because their previous ownership and delivery are unknown. A clean checkout alone is
|
|
265
|
-
insufficient proof of availability.
|
|
296
|
+
insufficient proof of availability. Slots outside the managed root appear as retired and are never
|
|
297
|
+
reused or counted toward the limit; offer `worktree.reconcile` with `retireLegacy:true` only when
|
|
298
|
+
the user wants the old folders gone.
|
|
266
299
|
|
|
267
300
|
When prior work has finished, call `worktree.reconcile` on that checkout with `releaseUnowned:true`
|
|
268
301
|
and the conversation language. Its native question binds the decision to the checkout, source and
|
|
@@ -270,6 +303,13 @@ ownership generation. Cancellation leaves it protected. Git reservation, dirty f
|
|
|
270
303
|
Git operations and pending work are checked again before release. A changed source requires a new
|
|
271
304
|
inspection and answer. This recovery never deletes files or branches or interrupts another agent.
|
|
272
305
|
|
|
306
|
+
When a recorded checkout is gone or can no longer be read as a Git worktree — its `.git` was
|
|
307
|
+
deleted, or the repository it was linked to was removed — `worktree.list` names `worktree.reconcile`
|
|
308
|
+
with `forgetUnreadable` and that task's `externalTaskId`, called from a working checkout of the same
|
|
309
|
+
project. After its own native approval Engineering Memory only stops tracking that entry, so it
|
|
310
|
+
stops counting toward the limit; no file in the folder is moved or deleted, the branch and the task
|
|
311
|
+
stay, and a readable checkout is still freed with `worktree.release`.
|
|
312
|
+
|
|
273
313
|
If the running client predates the pool tools, inspect `git worktree list` and the existing task
|
|
274
314
|
reservations before any explicit allocation. Do not treat an unavailable tool or an empty registry
|
|
275
315
|
as an empty filesystem. Preserve uncertain checkouts and report the client limitation.
|
|
@@ -51,7 +51,7 @@ always-running worker. Background execution must use the host's permitted facili
|
|
|
51
51
|
|
|
52
52
|
## Revision scope and review
|
|
53
53
|
|
|
54
|
-
Knowledge is layered. `scope: product` proposes a change to the shared engineering core that every organization reads, `scope: organization` proposes one that only this organization reads and which hides the product text for that key, and `scope: project` proposes a record for this project alone. The nearest layer wins when context is delivered. A product-scope proposal is the way a developer improves the product itself from inside their own project, and it requires nothing but the skill.
|
|
54
|
+
Knowledge is layered. `scope: product` proposes a change to the shared engineering core that every organization reads, `scope: organization` proposes one that only this organization reads and which hides the product text for that key, and `scope: project` proposes a record for this project alone. The nearest layer wins when context is delivered. A product-scope proposal is the way a developer improves the product itself from inside their own project, and it requires nothing but the skill. Product rules change only through a product release, never by approving a proposal in a chat, so a product-scope proposal stays inactive as input for the next release; it never blocks the contributor's task from verifying or closing. A shipped product release that republishes or withdraws the resource closes the proposals still waiting on it as superseded, with a note naming the release.
|
|
55
55
|
|
|
56
56
|
An architecture template is organization-scoped source, not prose. When a task establishes or changes a shared architecture structure that the templates carry, the template is stale and needs its own revision. Its manifest and content must stay in step: one `files[]` entry per `## file:` block, same path, byte count, SHA-256 and order. See `scaffolding.md`.
|
|
57
57
|
|
|
@@ -61,7 +61,13 @@ When a revision changes which stacks receive the resource, send the complete sel
|
|
|
61
61
|
|
|
62
62
|
Permanent proposals remain inactive until an authorized user explicitly approves them. Call `memory.review_proposal` only after receiving that decision through the native questionnaire.
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
An approval response carries `revisionId`, `resourceId`, `revisionNumber` and, when the proposal belongs to a task, `reconcileEntry`. After approval, pass `reconcileEntry` straight to `task.reconcile` rather than searching catalog or read_revisions for the revision that was just created; `revisionId` on the entry is optional and is resolved from the approved proposal when left out.
|
|
65
|
+
|
|
66
|
+
`memory.propose_revision` and `memory.list_proposals` report `reviewableByCaller` for each proposal. Ask the user for approval only when it is true. When it is false, the user in front of you cannot approve it, so do not ask: report the proposal as submitted, with its id, to the `reviewPath` it names — `product_release` for the product release, `organization_owner` for an owner of the organization, `project_maintainer` for an owner or maintainer of the project — and carry on with the task.
|
|
67
|
+
|
|
68
|
+
`metadata` on a proposal carries only the structured fields the resource kind defines, such as a project profile's `pathRoles`, `architecture` and `resourceDiscovery`, `appliesToStacks`, or an architecture template's manifest. `classification`, `containsPii`, `containsSecrets` and `resourceDescriptor` are computed by the server; sending them back from a served revision is harmless and they are ignored.
|
|
69
|
+
|
|
70
|
+
A proposal created in an earlier session, or created by a bundle import, is found with `memory.list_proposals`. It reports every proposal still waiting for review that this project or its organization can reach, with a `reasonPreview`, the current and base revision numbers, and whether the proposal now needs a rebase. Narrow a long list with `taskId`, `scope` or `status` (defaults to pending); page through the rest with `limit` and `afterId`, set to the response's `nextAfterId` to continue — `null` means nothing more is waiting. Pass a single `proposalId` to read that proposal's full `reason` and proposed text before putting the decision to the user. Never present a decision the user cannot see the content of, and never approve on the strength of the preview alone. Approval makes the current context lease stale. Call `context.refresh`, reread the changed rule, prepare a new change lease, and rerun affected validation.
|
|
65
71
|
|
|
66
72
|
A flow record is canonical in the same way a screen record is, and covers what no single screen knows: what the flow is for, every entry point with the screen or route it starts from, the ordered steps each naming its screen record and the Figma node it came from, the tracker that owns it and exactly what data that tracker carries or one sentence on why no tracker is needed, the branch points and what decides them, where the flow returns when it finishes, what happens when the user abandons it halfway, and the questions still open.
|
|
67
73
|
|
|
@@ -69,7 +75,7 @@ A `figma_reference` record is JSON, not prose: the current file key, the file UR
|
|
|
69
75
|
|
|
70
76
|
Screen logic is canonical in the backend. A screen revision should cover purpose, route, entry point, inputs, outputs, viewmodel observables, actions and computed values, tracker and global state, services and business codes, loading, empty and error behavior, Figma frames and components, localization keys, navigation, tests, constraints, selectors, and evidence.
|
|
71
77
|
|
|
72
|
-
Component mappings should cover the Flutter symbol and path, public API, purpose, states, design tokens, responsive behavior, Figma file and exact node IDs, assets, usage guidance, tests, selectors, and evidence. Mark missing or partial Figma evidence instead of inventing node IDs.
|
|
78
|
+
Component mappings should cover the Flutter symbol and path, public API, purpose, states, design tokens, responsive behavior, Figma file and exact node IDs, assets, usage guidance, tests, selectors, and evidence. Mark missing or partial Figma evidence instead of inventing node IDs. A `component_mapping` record is for the project's actual shared component area — something a second screen really imports. A widget that lives inside a screen's own `widgets` folder is part of that screen's `screen_logic` record instead, however much behaviour it carries; do not open a permanent component record for it.
|
|
73
79
|
|
|
74
80
|
A backend project records three kinds instead of screens and components, and which kinds a project has is declared in its project profile rather than assumed.
|
|
75
81
|
|
|
@@ -116,6 +116,21 @@ authentication, runtime/toolchain and cross-module/end-to-end flows. Record sour
|
|
|
116
116
|
behavior, inference and unknown historical reasons separately. Never fabricate historical tasks,
|
|
117
117
|
prior developer decisions or reasons the code cannot establish.
|
|
118
118
|
|
|
119
|
+
Adoption and refresh also record, in the project profile, the conventions the engineering rules
|
|
120
|
+
leave to the project: which layer shows a failure's message and whether transport failures are
|
|
121
|
+
handled centrally, which presenters show sheets, dialogs and toasts (static entry points on the
|
|
122
|
+
shared presenter), where typed request and response models live, and the folders that hold enums,
|
|
123
|
+
extensions and other shared
|
|
124
|
+
kinds. Record `metadata.pathRoles` as well: a map from each role the shipped rules govern (view,
|
|
125
|
+
state-object, service, transfer-object, enum, extension, presenter, component, flow, localization,
|
|
126
|
+
style, test) to the repository globs that hold it. `context.prepare_change` binds each changed path to
|
|
127
|
+
the rules of its roles through this map, so a path no glob maps is reviewed only against the rules the
|
|
128
|
+
agent names. A screen's own `widgets` folder carries the view role of the screen it belongs to, not
|
|
129
|
+
the component role; the component role, and the `component_mapping` kind in `resourceDiscovery`, are
|
|
130
|
+
for the project's actual shared component area, wherever the architecture places it — never for a
|
|
131
|
+
widget only one screen imports. A refresh rechecks the map and the conventions against the current
|
|
132
|
+
tree and proposes a profile revision when either has drifted.
|
|
133
|
+
|
|
119
134
|
1. `memory.sync_start` opens a durable native confirmation bound to the mode, committed source hash
|
|
120
135
|
and previousRunId/rework behavior. Only acceptance starts the run. It snapshots the selected HEAD
|
|
121
136
|
or explicit ref, uploads every manifest page with exclusion reasons and seals the inventory.
|
|
@@ -170,8 +185,10 @@ remaining units and decisions durably.
|
|
|
170
185
|
|
|
171
186
|
The core pack is intentionally bounded. Linked contracts remain inline while they fit the budget; when
|
|
172
187
|
the response reports `additionalMemory`, read the full authorized catalogue with
|
|
173
|
-
`memory.catalog({projectId, includeLinked: true, afterId, limit})`,
|
|
174
|
-
|
|
188
|
+
`memory.catalog({projectId, includeLinked: true, afterId, limit})`, passing each page's `nextAfterId`
|
|
189
|
+
as the next `afterId` until it comes back null, then use the current session with
|
|
190
|
+
`memory.read_revisions` for the exact revision IDs needed by the task; it returns what fits one
|
|
191
|
+
response and names the rest in `remainingRevisionIds`. Do not assume the core pack is
|
|
175
192
|
exhaustive. `memory.catalog` is the paging surface for linked project memory; it does not grant access
|
|
176
193
|
or change project links.
|
|
177
194
|
|
|
@@ -12,7 +12,17 @@ Fixed-choice answers can be replayed from the local receipt. Free text is return
|
|
|
12
12
|
|
|
13
13
|
Read pending questions returned by `session.entry` and `session.resume`. Call `questionnaire.resume` for the question belonging to this work. Do not silently adopt a pending question from another task. Resuming uses the original question; do not rewrite its options under the same identifier.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
A question belongs to the task the chat was working on when it was asked. That covers the forms
|
|
16
|
+
`task.branch` and `project.set_git_preferences` open and every `questionnaire.ask` made after
|
|
17
|
+
`session.bootstrap` or `session.resume` in the same folder. `pendingQuestionnaires` holds only the
|
|
18
|
+
current task's own questions and any question with no task owner (a project-level decision, or
|
|
19
|
+
one asked before any task started). Every other task's question appears under
|
|
20
|
+
`otherTasksPendingQuestionnaires` as a one-line summary naming its task: enough to recognise,
|
|
21
|
+
never enough to answer here. Resume it only in the chat working on that task.
|
|
22
|
+
|
|
23
|
+
Only an explicit, valid accepted response answers a question. A timeout, dismissed form, empty response, invalid response, connection loss or ended turn is not an answer. These events leave the decision pending. Never substitute the recommended choice or treat silence as consent. The form's cancel or decline button dismisses the form; if abandoning the work is a meaningful decision, offer it as an explicit answer in the question. A question left pending this way stays pending; show it again once, on the user's next message, and then wait rather than re-asking on every following turn.
|
|
24
|
+
|
|
25
|
+
State the decision and one concrete example directly in the question's `message`, in the language the user is writing in. Never put a resource key, hash, task ID or other internal identifier in a question's text — it explains nothing to the person answering and only makes the question harder to read.
|
|
16
26
|
|
|
17
27
|
Never use request_user_input_async for a required decision: it does not wait for an answer. Do not end a turn while a required native form is still awaiting the user. Work that does not depend on the answer may continue. If the host removes the form or interrupts the tool call, retain the pending decision and resume that same question when the user returns. Do not automatically reopen dismissed forms in a loop, or use repeated sleeps to claim that a host timer has been disabled.
|
|
18
28
|
|
|
@@ -24,12 +34,11 @@ Never replace the questionnaire with a chat instruction such as 'type this', 're
|
|
|
24
34
|
|
|
25
35
|
When a durable MCP form cannot be displayed, read its `hostFallback` or call `questionnaire.resume` with `presentation: host_native` to get the original question without reopening the MCP form. Display the same question, all choices and notices through a blocking native control only if the host permits that control for this decision. After an actual native answer, call `questionnaire.answer_from_host` with the unchanged questionnaireId, requestKey and contentHash, the hostTool name and the returned choice/text. This is an agent-reported relay, not MCP transport attestation. Then retry the owning operation; its authority, content and version checks still apply. Never relay prose consent, a default, an asynchronous response or a cancelled/declined/missing answer. Decline alone is not proof that the host cannot display forms. Keep the decision pending if no permitted native control can represent it.
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
edit requires reading current preferences and fresh decisions for the new version.
|
|
37
|
+
Branch preferences are one form bound to the exact supplied values and expectedVersion. Relay
|
|
38
|
+
its answers once, keyed by role, then make the returned retry; the save applies only while the
|
|
39
|
+
version and authority still match. An answer to some other question, such as a prose "yes to
|
|
40
|
+
all branches", is never relayed into it. A concurrent edit requires reading current preferences
|
|
41
|
+
and a fresh form for the new version.
|
|
33
42
|
|
|
34
43
|
Map the native result to the original option id only when the selected label identifies it
|
|
35
44
|
unambiguously; never substitute or omit choices to fit a host limit. If all choices and required
|
|
@@ -44,6 +53,42 @@ clients must restart after an upgrade to read the new relay receipt field; old r
|
|
|
44
53
|
readable by the new client. No server or SDK can independently prove an agent's claim that a
|
|
45
54
|
host-native form was shown; agents must use only the actual native result they observed.
|
|
46
55
|
|
|
56
|
+
Claude Code declines the MCP form today, so each attempt costs several seconds for nothing.
|
|
57
|
+
After the first decline in this session, pass `presentation: host_native` on `questionnaire.ask`
|
|
58
|
+
and on any owning tool that opens a question (`task.branch`, `project.set_git_preferences`) to
|
|
59
|
+
go straight to `hostFallback`. This is a per-session choice, not a permanent verdict on the
|
|
60
|
+
host: offer the MCP form again in a fresh session, since decline alone is not proof the host
|
|
61
|
+
cannot display one.
|
|
62
|
+
|
|
63
|
+
Some forms carry several related questions in one durable record. The `task.branch` start form
|
|
64
|
+
asks where the task works, which branch it starts from and the branch name; the
|
|
65
|
+
`project.set_git_preferences` form asks one question per role. Their `hostFallback` carries a
|
|
66
|
+
`questions` array instead of a single `options` array. AskUserQuestion takes at most 4
|
|
67
|
+
questions per call, so show them in as few calls as needed and relay every answer together in
|
|
68
|
+
one `questionnaire.answer_from_host` call, as `answers` keyed by each question's id. A choice
|
|
69
|
+
that needs text, such as a custom branch name, carries it as that answer's `text`. A record can
|
|
70
|
+
also declare `terminalChoices`: picking one of those answers (for example "Not now") makes
|
|
71
|
+
every other question in that form optional.
|
|
72
|
+
|
|
73
|
+
A relayed answer that resolves an owner-bound question (one opened by `task.branch` or
|
|
74
|
+
`project.set_git_preferences`) returns `retry: {tool, arguments}`: the exact call that reaches
|
|
75
|
+
the same decision again, with its authority and version checks intact. Make that call next
|
|
76
|
+
instead of re-deriving its arguments.
|
|
77
|
+
|
|
78
|
+
## Deferred decisions
|
|
79
|
+
|
|
80
|
+
"Not now" is not a normal answer: it is an explicit, resumable on-hold state, never replayed as
|
|
81
|
+
if the user had approved something. A deferred `task.branch` question returns
|
|
82
|
+
`{status: 'deferred', allocated: false, reconsider}`: nothing was created, reserved or saved.
|
|
83
|
+
Say in one line that starting that task is on hold, continue only read-only work, and call
|
|
84
|
+
`reconsider` exactly as given — not a hand-typed retry — only once the user actually asks to
|
|
85
|
+
start it; it carries a fresh `decisionAttempt` and asks the form again. Never reopen the same
|
|
86
|
+
deferred question in the same turn on your own initiative.
|
|
87
|
+
|
|
88
|
+
`session.entry` lists recent on-hold task starts under `deferredTaskStarts`, each with its own
|
|
89
|
+
`reconsider` call. Mention one only if the user brings up that task; a deferred start sitting
|
|
90
|
+
there is not itself a reason to interrupt other work.
|
|
91
|
+
|
|
47
92
|
## Authentication
|
|
48
93
|
|
|
49
94
|
When no Engineering Memory session exists, ask whether to sign in, create an account, or skip Engineering Memory for this task. Explain that skip is permitted only for an unbound repository. After sign in or sign up is selected, let the bridge open the browser authentication flow. Never ask for a password in the native questionnaire or chat. If a browser link is expired, rejected, already used, or otherwise unusable, call `auth.signin_browser` with `restart: true` and present only the newly returned URL.
|
|
@@ -74,6 +119,8 @@ Once the organization is chosen, call `project.list` and offer that organization
|
|
|
74
119
|
|
|
75
120
|
The normal list omits archived projects. If repository entry, setup, binding or task open reports `project.restore`, use the project id and expected version returned with that refusal and restore it before continuing. If it reports `project.member_list`, the current member cannot restore the project: call that operation to check the available roles and explain that the versioned restore requires project-owner authority. Do not identify a person, reconstruct an identity from an account identifier or disclose contact details. If an owner does not yet have the project id and expected version, call `project.list` with `includeArchived: true` and let them select the archived project. Archive state never authorizes creating a duplicate project.
|
|
76
121
|
|
|
122
|
+
When the user picks an existing project and binding it is refused because the project lives in another repository, do not offer to create a new project for the same code. An owner is asked once, through the form `project.move_repository` opens, whether the project moves to this repository; the form already says which repository it leaves, which one it joins and how many tasks, memory records and work items stay with it, so add no second question of your own. Anyone else is told that a project owner runs the move, and is not asked anything.
|
|
123
|
+
|
|
77
124
|
Ask both questions again whenever a chat starts in a repository whose binding you have not confirmed in this session.
|
|
78
125
|
|
|
79
126
|
## Switching Organization or Project
|
|
@@ -129,7 +176,7 @@ Ask for the registered email and intended role. Show owner, maintainer, member,
|
|
|
129
176
|
|
|
130
177
|
## Branch
|
|
131
178
|
|
|
132
|
-
|
|
179
|
+
Call `task.branch` once for every new write or scaffold task, on any branch, and before a read-only task transitions to writing. Do not ask these questions yourself first: its one form asks where the task works (a separate managed worktree, this folder when it is clean, this folder after the task holding it moves out, or the branch already checked out), which branch it starts from, and the branch name, with a suggestion that follows the returned naming convention. Pass the exact `externalTaskId`. Pass `name`, `base` or `keepCurrent` only when the user already stated them, so the form skips that question; never pass a `worktreePath`. A task whose branch already exists resumes its recorded task instead of starting a new one. Continue in the returned `repoRoot`. Retries and handoffs reuse the stored answer. Read-only analysis does not create a branch or consume another task's answer.
|
|
133
180
|
|
|
134
181
|
## Flow Entry and Exit
|
|
135
182
|
|
|
@@ -182,7 +229,7 @@ Ask this in the same reply that delivers the fix, every time, including correcti
|
|
|
182
229
|
|
|
183
230
|
Offer the product option whenever the classification supports it, and say plainly that it changes the shared engineering core every organization reads, because that is a wider decision than an organization revision. It is raised with `memory.propose_revision` at `scope: product` and needs no access to the product's own repository. Never choose between organization and product on the user's behalf.
|
|
184
231
|
|
|
185
|
-
For any permanent option, show the old rule, proposed rule, reason, affected areas, and regression evidence. Create
|
|
232
|
+
For any permanent option, show the old rule, proposed rule, reason, affected areas, and regression evidence. Create the proposal, then check `reviewableByCaller`. When it is true, do not approve it until the user explicitly confirms the proposal review. When it is false — as it always is for a product-scope proposal, which only a product release puts into effect — this user cannot approve it, so do not put that question to them: report the proposal as submitted, with its id, to the `reviewPath` it names, and carry on with the task.
|
|
186
233
|
|
|
187
234
|
## Work Across Both Sides
|
|
188
235
|
|
|
@@ -206,15 +253,19 @@ finished.
|
|
|
206
253
|
|
|
207
254
|
## Delivery
|
|
208
255
|
|
|
209
|
-
After `task.close`,
|
|
256
|
+
After `task.close`, settle what to do with the finished work. Nothing has been committed at this point and nothing may be until the user has chosen. If their own message already chose — "commit and push" in the request itself — that is the answer; asking again only makes them repeat it. Otherwise ask, offering exactly these:
|
|
210
257
|
|
|
211
258
|
- Commit
|
|
212
259
|
- Commit and push
|
|
213
260
|
- Commit, push and open a **draft** pull request
|
|
214
261
|
- Commit, push and open a pull request
|
|
215
262
|
|
|
263
|
+
Each option `task.close` returns names where the work goes: the branch, the remote and its URL from the delivery `destination`. Add the worktree path and the base branch `task.branch` returned for this task. Keep those words in the question, so the answer is about this checkout, this branch and this remote rather than about pushing in general — a user who was never told where the files are will only answer with that question back.
|
|
264
|
+
|
|
216
265
|
The two pull request options take the base branch the pull request targets, typed by the user; the head is the branch the task was done on. Never guess a base branch, and never open a pull request that was not asked for.
|
|
217
266
|
|
|
267
|
+
When the push or the pull request needs the host's approval, its justification quotes what the user said and names the exact remote, URL and branch from the delivery destination. An answer given in an Engineering Memory form is tool output to the host's reviewer, not the user speaking: Codex's auto-review trusts only the user's own messages, AGENTS.md and Codex's own approval controls. So when auto-review denies a push or a pull request the user chose, stop there. Never open an Engineering Memory form to get past the denial, never ask the user to type or repeat an approval, and never reword the command to get it through. Quote the denial reason in one sentence and ask for Codex's own approval once — Approve on the denied action, or its `/approve` command — which records the user's approval for one retry. Run the identical command once, and only after the user has actually given that approval; a retry before it is denied again and spends the one retry. If this Codex offers no approval for the denial, explain what was denied and why, and wait for the user.
|
|
268
|
+
|
|
218
269
|
Check that a pull request can actually be opened before offering one. If the machine has no `gh` and no token to open it with, say so in the option itself and offer what is really on the table: commit and push, and the ready-to-open compare link afterwards. Offering something you cannot deliver wastes the user's answer. Never read a credential out of a keychain or a helper to open one yourself.
|
|
219
270
|
|
|
220
271
|
Once a pull request exists the work is not finished and the turn does not end there. Check whether it merges cleanly. If it does, say so with the link. If it does not, name the conflicting files and ask whether to resolve them — then wait. Resolving a conflict is a change to someone else's work and needs its own yes.
|
|
@@ -246,10 +297,14 @@ project.rework_plan confirms an exact approved target and phase digest after lea
|
|
|
246
297
|
reviewAttempt distinguishes a new requested review from a retry of a previously deferred batch. Do not
|
|
247
298
|
turn defer into approve or loop new attempts without the user's intent to reconsider.
|
|
248
299
|
|
|
300
|
+
task.verify with `waivers` asks, for each one, whether the required UI check may be skipped for the
|
|
301
|
+
exact changed paths and reason; pass language and repeat the same call to resume it. A require answer
|
|
302
|
+
keeps the check required and verifies nothing.
|
|
303
|
+
|
|
249
304
|
The initial context pack may be budgeted. If it reports `additionalMemory`, use
|
|
250
305
|
`memory.catalog({projectId, includeLinked: true, afterId, limit})` to page all authorized linked
|
|
251
|
-
resource headers,
|
|
252
|
-
needed. Never assume that the core pack contains every linked record. Source inspection should begin
|
|
306
|
+
resource headers, passing each page's `nextAfterId` as the next `afterId` until it is null, then call
|
|
307
|
+
`memory.read_revisions` with the current session for the exact revisions needed. Never assume that the core pack contains every linked record. Source inspection should begin
|
|
253
308
|
with the bounded orientation summary from `project.inspect`; use paged committed-source reads for the
|
|
254
309
|
units being learned.
|
|
255
310
|
|
|
@@ -281,15 +336,24 @@ withdrawing it. Language changes never broaden the approved source, mode or prev
|
|
|
281
336
|
|
|
282
337
|
## Worktree and branch decisions
|
|
283
338
|
|
|
284
|
-
`task.branch` owns
|
|
339
|
+
`task.branch` owns the native start form. Its answer binds the external task identifier, the folder, the selected source and the branch name, and a fetch pins the exact commit. A cancelled or dismissed form allocates nothing; repeat the same call to resume it. If the folder, its commit or the task holding it changed in the meantime, the same call asks a fresh form, because the earlier answer was given about something else. Do not select a different source to recover a timeout. When the chosen remote branch cannot be fetched, `task.branch` itself asks whether to continue from the local commit. A custom branch name that already exists or is not a valid Git name is refused without creating anything, with the `decisionAttempt` that asks the form again. Show actual names from project.git_preferences, never infer develop/test/prod meaning from a branch name.
|
|
285
340
|
|
|
286
|
-
Shared preferences
|
|
341
|
+
Shared preferences are confirmed in one native form: `project.set_git_preferences` asks every
|
|
342
|
+
supplied role (development, production, test) as its own question in a single multi-question
|
|
343
|
+
record, then saves every role answered "save this choice" in one PUT at `expectedVersion`. A
|
|
344
|
+
role answered "leave unchanged" is simply not included in that PUT; it stays exactly as it was
|
|
345
|
+
and unanswered, and reconsidering it later is a plain retry with a new `decisionAttempt`, not a
|
|
346
|
+
separate on-hold state. Explicitly absent production/test values are still answers. Members
|
|
347
|
+
without canManage receive a task-specific branch choice, never an administrator question they
|
|
348
|
+
cannot apply. The same task's accepted decision stays valid across Codex/Claude handoff after
|
|
349
|
+
pause; return to session.resume and the recorded repoRoot.
|
|
287
350
|
|
|
288
351
|
For task.branch and project.set_git_preferences, retries keep the same decisionAttempt (omit it on the first attempt). When the user explicitly reconsiders an answered deferral, use a new positive decisionAttempt to open a fresh native decision. Never reinterpret the old deferral as consent.
|
|
289
352
|
|
|
290
353
|
## Legacy worktree availability
|
|
291
354
|
|
|
292
|
-
|
|
355
|
+
`task.branch` reuses a safe slot before creating another directory; `worktree.list` explains which
|
|
356
|
+
checkouts stay protected when the pool is full. An unregistered legacy checkout
|
|
293
357
|
has unknown prior ownership and delivery, even if Git reports clean. `worktree.reconcile` with
|
|
294
358
|
`releaseUnowned:true` and `language` opens the native availability question for that exact checkout,
|
|
295
359
|
source commit and generation. The user confirms prior work and delivery have finished; current Git
|
|
@@ -297,5 +361,12 @@ safety checks still decide whether reuse is possible. Cancellation or choosing t
|
|
|
297
361
|
release it. Resume the same form via the same operation. A changed source/generation needs a fresh
|
|
298
362
|
decision, and an old answer cannot release a newly owned task.
|
|
299
363
|
|
|
364
|
+
A checkout that is gone, or that can no longer be read as a Git worktree, is left with
|
|
365
|
+
`worktree.reconcile` and `forgetUnreadable: {"externalTaskId": "..."}` from a working checkout of the
|
|
366
|
+
same project: its native question names the task and the folder name, says that Engineering Memory
|
|
367
|
+
only stops tracking it and touches no file, and warns when that task closed with its delivery
|
|
368
|
+
unsettled. Cancelling or keeping it leaves it tracked, and a readable checkout is refused there and
|
|
369
|
+
freed with `worktree.release` instead.
|
|
370
|
+
|
|
300
371
|
If the user later reconsiders a preserved legacy checkout, repeat `worktree.reconcile` with a new
|
|
301
372
|
`decisionAttempt`; never reinterpret the previous preserve answer as approval.
|