@uchibeke/gallivant 0.2.0 → 0.3.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.
Files changed (133) hide show
  1. package/README.md +120 -60
  2. package/SECURITY.md +9 -0
  3. package/bin/gallivant.mjs +41 -14
  4. package/bin/promote-bitbucket.mjs +40 -0
  5. package/bin/task-pr.mjs +9 -0
  6. package/bin/task-review.mjs +15 -0
  7. package/docs/README.md +6 -2
  8. package/docs/agent-setup.md +55 -41
  9. package/docs/architecture.md +11 -3
  10. package/docs/bitbucket-promotion.md +69 -0
  11. package/docs/bitbucket-review.md +330 -0
  12. package/docs/chains.md +30 -1
  13. package/docs/codex-cloud.md +7 -0
  14. package/docs/codex-review.md +50 -0
  15. package/docs/commands.md +103 -14
  16. package/docs/config.md +45 -13
  17. package/docs/execution.md +1 -1
  18. package/docs/first-party-review.md +266 -0
  19. package/docs/git-hosts.md +294 -0
  20. package/docs/guardrails.md +56 -0
  21. package/docs/install.md +137 -67
  22. package/docs/launch.md +7 -4
  23. package/docs/network-recovery.md +9 -0
  24. package/docs/openai-agents.md +3 -2
  25. package/docs/prd.md +23 -2
  26. package/docs/preparation.md +2 -2
  27. package/docs/release-qualification.md +1 -1
  28. package/docs/releasing.md +46 -22
  29. package/docs/task-delivery.md +5 -5
  30. package/docs/task-profiles.md +537 -0
  31. package/docs/tasks.md +59 -197
  32. package/lib/attribution.mjs +7 -3
  33. package/lib/block.mjs +10 -1
  34. package/lib/chains.mjs +101 -30
  35. package/lib/codex_cloud.mjs +2 -1
  36. package/lib/codex_cloud_input.mjs +15 -6
  37. package/lib/codex_cloud_result.mjs +1 -1
  38. package/lib/commands/doctor.mjs +22 -453
  39. package/lib/commands/init.mjs +281 -28
  40. package/lib/commands/register.mjs +81 -13
  41. package/lib/commands/resume.mjs +5 -1
  42. package/lib/commands/review.mjs +66 -0
  43. package/lib/commands/serve.mjs +8 -0
  44. package/lib/commands/start.mjs +30 -11
  45. package/lib/commands/status.mjs +18 -2
  46. package/lib/commands/task.mjs +9 -1
  47. package/lib/commands/up.mjs +19 -11
  48. package/lib/commands/why.mjs +7 -3
  49. package/lib/config.mjs +42 -9
  50. package/lib/context.mjs +28 -21
  51. package/lib/control.mjs +21 -6
  52. package/lib/credential.mjs +20 -14
  53. package/lib/deliverable.mjs +2 -0
  54. package/lib/events.mjs +96 -53
  55. package/lib/execution.mjs +132 -11
  56. package/lib/execution_preparation.mjs +16 -1
  57. package/lib/execution_result.mjs +26 -5
  58. package/lib/first_party_review.mjs +618 -0
  59. package/lib/fs.mjs +10 -3
  60. package/lib/gh.mjs +144 -56
  61. package/lib/git.mjs +133 -32
  62. package/lib/git_diff.mjs +121 -0
  63. package/lib/harness.mjs +14 -10
  64. package/lib/host_contract.mjs +71 -0
  65. package/lib/host_definitions.mjs +22 -0
  66. package/lib/hosts/bitbucket.mjs +553 -0
  67. package/lib/hosts/bitbucket_contract.mjs +57 -0
  68. package/lib/hosts/bitbucket_credential.mjs +62 -0
  69. package/lib/hosts/bitbucket_review.mjs +44 -0
  70. package/lib/hosts/bitbucket_review_producer.mjs +50 -0
  71. package/lib/hosts/github.mjs +57 -0
  72. package/lib/hosts/github_contract.mjs +27 -0
  73. package/lib/hosts/github_doctor.mjs +482 -0
  74. package/lib/hosts/github_review_summary.mjs +38 -0
  75. package/lib/hosts/transport_doctor.mjs +35 -0
  76. package/lib/hosts.mjs +130 -0
  77. package/lib/learning_qualification.mjs +1 -1
  78. package/lib/notify.mjs +1 -1
  79. package/lib/openai_agents_input.mjs +7 -2
  80. package/lib/openai_agents_sources.mjs +16 -8
  81. package/lib/plugin_package.mjs +13 -1
  82. package/lib/pr_operation.mjs +41 -0
  83. package/lib/prd.mjs +18 -3
  84. package/lib/preparation_codex.mjs +6 -1
  85. package/lib/probe.mjs +6 -4
  86. package/lib/process.mjs +24 -0
  87. package/lib/promotion.mjs +132 -0
  88. package/lib/promotion_git.mjs +63 -0
  89. package/lib/prompt.mjs +16 -13
  90. package/lib/provider_health.mjs +14 -3
  91. package/lib/repository.mjs +68 -0
  92. package/lib/repository_preflight.mjs +11 -0
  93. package/lib/review_evaluation.mjs +25 -0
  94. package/lib/review_evidence.mjs +500 -0
  95. package/lib/review_history.mjs +73 -0
  96. package/lib/review_image.mjs +145 -0
  97. package/lib/review_link.mjs +12 -0
  98. package/lib/review_policy.mjs +132 -0
  99. package/lib/review_policy_contract.mjs +18 -0
  100. package/lib/review_state.mjs +13 -0
  101. package/lib/review_visual.mjs +94 -0
  102. package/lib/runtime-environment.mjs +6 -0
  103. package/lib/runtime_node.mjs +3 -2
  104. package/lib/runtime_state.mjs +39 -5
  105. package/lib/sources/prd.mjs +6 -3
  106. package/lib/state.mjs +137 -24
  107. package/lib/task_completion.mjs +7 -3
  108. package/lib/task_contract.mjs +201 -27
  109. package/lib/task_delivery.mjs +9 -2
  110. package/lib/task_delivery_contract.mjs +13 -9
  111. package/lib/task_delivery_view.mjs +18 -2
  112. package/lib/task_environment.mjs +13 -1
  113. package/lib/task_limits.mjs +13 -0
  114. package/lib/task_model_config.mjs +99 -0
  115. package/lib/task_model_route.mjs +243 -0
  116. package/lib/task_prepare.mjs +21 -8
  117. package/lib/task_profile.mjs +1875 -0
  118. package/lib/task_repository_context.mjs +65 -0
  119. package/lib/tasks.mjs +252 -51
  120. package/lib/tick.mjs +373 -98
  121. package/lib/time.mjs +1 -0
  122. package/lib/tracker/pages.mjs +19 -5
  123. package/lib/tracker/review.mjs +21 -0
  124. package/lib/tracker_process.mjs +8 -1
  125. package/lib/verification_sandbox.mjs +2 -1
  126. package/lib/work_artifacts.mjs +79 -0
  127. package/npm-shrinkwrap.json +12 -3
  128. package/package.json +9 -3
  129. package/templates/plugin/task-delivery.SKILL.md +17 -10
  130. package/templates/plugin/task-intake-contract.json +18 -0
  131. package/templates/plugin/task-profile-requests.mjs +1448 -0
  132. package/templates/tracker/tracker.css +3 -0
  133. package/templates/tracker/tracker.js +38 -0
@@ -0,0 +1,537 @@
1
+ # Typed delivery profiles
2
+
3
+ New natural-language tasks take delivery permission from one typed profile
4
+ that the owner selects when the task is prepared. Request text, answers,
5
+ selected documents and model output can describe the implementation and can
6
+ raise clarification questions. None of them can add, widen or remove an
7
+ external action. This replaces the free-text publication grammar for new
8
+ tasks; saved tasks keep the contract they were created with (see
9
+ [historical tasks](#historical-tasks)).
10
+
11
+ ```sh
12
+ gallivant task "Add CSV export for the widget table. Open one PR for review." \
13
+ --harness claude --delivery pull_request --completion-target delivery_ready --json
14
+ ```
15
+
16
+ The profile is saved with the task as:
17
+
18
+ ```json
19
+ {"version":1,"action":"pull_request","target":{"kind":"github-pull-request","repository":"acme/widget","base":"main"},"completion":{"condition":"delivery_ready"}}
20
+ ```
21
+
22
+ | Field | Values | Meaning |
23
+ | --- | --- | --- |
24
+ | `action` | `pull_request` | The only supported action: implement on a task branch and open exactly one pull request for human review. `merge` and `deploy` are recognised so a request for them fails closed with a remedy; they need an explicit supported control and authorization that this release does not have. |
25
+ | `target` | resolved repository and base | Bound from the checkout and the usual base precedence when the task is created. Never taken from text. |
26
+ | `completion.condition` | `delivery_ready`, `integrated` | The same milestone as `--completion-target`. `integrated` keeps watching until someone else merges; it grants no merge authority. |
27
+
28
+ Omitting `--delivery` selects `pull_request`. The profile is immutable for a
29
+ saved request identity; changed retry input returns `TASK_CONFLICT`. The
30
+ structured `prepare` operation accepts `delivery: { action, completion }` and
31
+ the MCP `task_prepare` schema carries the same field. Capability discovery lists
32
+ the supported actions, so clients render those instead of their own list.
33
+
34
+ ## Clauses, mappings and the delivery screen
35
+
36
+ New tasks split request and answer text at sentence, semicolon and newline
37
+ boundaries outside paired quoted literals and code spans. Each material clause
38
+ has a stable identifier and its exact UTF-16 span in the immutable source text.
39
+ The task and review pin `fragmentVersion: 2`; saved tasks without that pin or
40
+ with version 1 retain their original line-based references, including later
41
+ answers. Unsupported versions remain inspectable and fail mutations closed with
42
+ a compatible-package or new-task recovery path. The planner must
43
+ map each fragment to generated criteria, to one explicit assumption listed in
44
+ the brief, or to a clarification question. Unmapped fragments trigger bounded
45
+ repair and can never validate or start.
46
+
47
+ When a clause asks for, forbids or conditions an external action, the planner
48
+ adds a literal reading as a typed `delivery` tag (`pull_request`, `merge`,
49
+ `deploy`, `release`, `publish`, `local_only`, `wait_for_approval`, or `restrict`
50
+ with the effects it forbids). The service compares that reading with the
51
+ profile:
52
+
53
+ - A covered reading (the profile already does exactly that, or the clause only
54
+ forbids effects the profile never performs) is recorded in the mapping. The
55
+ clause still keeps its implementation content in the criteria. A tag speaks
56
+ only for the terms it names, and only in the right sense: a requested action
57
+ speaks for terms outside a control word or under an affirmative one ("a
58
+ pull request is required"), a prohibition for terms under a negative one
59
+ ("do not", "except", "is not required"); control words bind within a
60
+ statement, across additive conjunctions. Contrast words such as "but" and
61
+ "however" start a new control scope. Both senses are retained when an action
62
+ occurs more than once: "Do not deploy; deploy to production" cannot be covered
63
+ by a prohibition tag. So "do not open
64
+ a pull request" tagged as a `pull_request` action, "merge the PR" tagged with
65
+ a `merge` prohibition, an escalating term the tag leaves out ("open a PR and
66
+ deploy it") and an effect term it leaves out under a control word ("open a
67
+ PR; do not push to main") all keep the screen's own clarification.
68
+ - An uncovered reading becomes the deterministic question `q-delivery-<clause>`.
69
+ Preparation pauses with `needs-input`; no readiness decision or implementation
70
+ starts until the owner answers.
71
+
72
+ New delivery question IDs are at most 64 characters. Longer clause identifiers
73
+ use a stable portable digest suffix instead of embedding their full answer
74
+ ancestry. Exact source spans and saved question ownership retain that ancestry.
75
+ Saved IDs within the 128-character public answer limit remain unchanged. If an
76
+ older pending delivery question exceeds that limit, explicitly resume the task
77
+ at its current revision: intake creates a bounded replacement and a fresh draft
78
+ revision without inference or implementation, preserving the old history and
79
+ accepted answers. Answer the replacement ID from the resumed result; readiness
80
+ and execution authorization remain unset. Conflicting saved ownership refuses
81
+ recovery and requires inspection before retrying.
82
+
83
+ Explicit PR destinations are also compared with the selected repository and
84
+ case-sensitive base, even if the planner supplies only a `pull_request` tag.
85
+ Planner tags retain named destinations as `target: { repository, base }`, with
86
+ either field optional; malformed targets are rejected and mismatches clarify.
87
+ "Open a PR against staging" under a `main` profile, or a PR in another
88
+ repository, pauses with a question naming the selected target. Ambiguous
89
+ destinations require clarification. Text cannot change the saved target; the
90
+ owner must resolve the scope or prepare a new task with the intended selection.
91
+ Canonical `gh pr create` commands retain `--base`/`-B` and `--repo`/`-R`
92
+ arguments under the same comparison, including equals, attached short flags,
93
+ quoted values and repeated flags. Title/body values and later commands cannot
94
+ supply a target flag. Missing, dynamic or ambiguous target arguments clarify;
95
+ matching explicit targets retain the normal PR reading. These arguments never
96
+ select or expand the saved target.
97
+ Attached quoted title/body/description values (`titled`, `named`, `called`,
98
+ `with title`, `with body`) are consumed as data before checking later targets.
99
+ Only actual target spans are unmasked; title/body text cannot add delivery terms
100
+ or supply a destination. Incomplete or ambiguous descriptive values clarify.
101
+ Parsing still stops at independent actions instead of borrowing their targets.
102
+ Explicit `--head`/`-H` selectors also retain a target conflict, including fork
103
+ refs, attached/equal values, dynamic values and `HEAD`. The managed task branch
104
+ is allocated after approval, so preparation cannot prove an explicit head names
105
+ it. Omit the head override to keep managed task-branch delivery. Title/body data,
106
+ feature examples, terminators and independent later commands do not select a
107
+ head. No profile or planner field can grant a different head branch.
108
+ Quoted target values stay visible only when attached to a PR target qualifier,
109
+ including paired repository/base values and leading qualifiers. Matching quoted
110
+ values keep the ordinary PR reading; mismatches retain the exact named target.
111
+ Other quoted examples and unrelated prepositional phrases remain product text.
112
+ Draft and dry-run PR creation modes (`--draft`/`-d`, `--dry-run`) remain
113
+ `pull_request_mode` conflicts: this profile creates a regular review PR. Explicit false boolean flags, title/body argument values,
114
+ read-only help for registry commands, product features and literal examples
115
+ retain their own meaning. Natural-language requests to create a draft PR also
116
+ require clarification; no planner tag can select a different creation mode.
117
+ The profile opens exactly one PR. Requests for two or more PRs, separate PRs,
118
+ a PR per component, repeated PR creation commands or another PR retain a
119
+ `pull_request_count` conflict even if the planner supplies a `pull_request` tag.
120
+ Target qualifiers must be attached to the PR instruction, including leading
121
+ targets and adjacent repository/base pairs. Separate actions such as "run the
122
+ tests on Linux" or "document it in docs/README.md" do not select a PR target.
123
+
124
+ Independently of the planner, a deterministic screen looks for delivery
125
+ vocabulary in each clause (pull request, merge, deploy, release, publish, push,
126
+ commit, branch, approval, review, wait, local-only). Paired straight or curly
127
+ single/double quotes and code spans are masked as literal examples. Apostrophes
128
+ in contractions and possessives do not open quotes. Imperative command spans
129
+ such as ``Run `npm publish` `` remain visible, including linked command lists;
130
+ product wording such as ``Document how to run `npm publish` `` stays literal.
131
+ A span whose
132
+ statement carries a control phrase before or after it (`Please do not ever
133
+ "open a pull request"`, `"Open a pull request" is forbidden`) is read as
134
+ the control's target; a control word inside another example never counts. Restriction forms such
135
+ as "leave the changes uncommitted", "unstaged", "keep all edits in the working
136
+ tree" or "unpublished" count as local-only. Control words bind within a statement: sentence and clause
137
+ punctuation ends one, but additive conjunctions and an introductory colon after the
138
+ control ("Do not: open a pull request") do not.
139
+
140
+ Terminal directions such as "stop after committing the changes", "stop once
141
+ committed" and "once committed, stop" also count as local-only: they withhold
142
+ the push and PR required by the profile. They require clarification even when
143
+ the planner omits a tag. Feature infinitives and database transaction commits
144
+ retain their product interpretation.
145
+
146
+ Wrapped request and answer lines are screened in the complete source statements
147
+ that touch their immutable spans. Source IDs, text and offsets remain unchanged;
148
+ context never crosses between the original request and an answer or between
149
+ separate answers. Sentence punctuation, blank lines and list-item boundaries
150
+ separate controls; paired literal spans retain their context across wrapped
151
+ lines. Shared statement windows are cached within each source.
152
+
153
+ The screen grants nothing:
154
+
155
+ - Existing-PR branch updates (`gh pr update-branch`, including `--rebase`)
156
+ retain the lifecycle conflict, including repository selectors. They do not
157
+ authorize updating an unrelated PR head.
158
+ - Workflow dispatch, enable/disable and existing Actions run rerun/cancel/delete
159
+ are unsupported external mutations, including canonical CLI commands and
160
+ imperative workflow wording. Active/passive Actions-run mutation prose and
161
+ quoted run references follow the same rule. Read-only list/view/watch commands,
162
+ features and literal examples retain their implementation meaning. Command coverage follows
163
+ [workflow commands](https://cli.github.com/manual/gh_workflow) and
164
+ [run commands](https://cli.github.com/manual/gh_run).
165
+ - Issue creation, editing, commenting, closing and reopening are unsupported
166
+ external mutations, including passive instructions, quoted issue references,
167
+ GitHub issue URLs and canonical `gh issue` write commands. Related writes
168
+ (deleting, assigning, locking, pinning and transferring issues, or creating
169
+ linked development branches) follow the same rule. They require clarification
170
+ even with an omitted or `pull_request` planner tag; no new profile action or
171
+ prohibition tag is granted. Read-only issue commands, issue templates,
172
+ product features and literal examples retain their implementation meaning.
173
+ Command coverage follows the [GitHub CLI issue commands](https://cli.github.com/manual/gh_issue).
174
+ - Release actions retain their unsupported delivery effect; product nouns such
175
+ as release notes, versions and metadata do not request that effect. Actual
176
+ release/publish lifecycle instructions, release asset writes and canonical
177
+ `gh release` mutations still require clarification. The shared command reader
178
+ keeps read-only release commands, option values and templates as data.
179
+ Feature infinitives, local resource release, and release-named refs/flags
180
+ retain implementation scope; explicit release prohibitions remain traceable.
181
+ - GitHub Discussion creation, edits and comment/reply writes are unsupported
182
+ external content mutations. List/view, help and quoted content retain scope.
183
+ - Nested repository autolink create/new/delete commands retain a repository
184
+ mutation conflict; list/view and help remain read-only, including parent and
185
+ child option values.
186
+ - Repository delete, archive/unarchive, rename, settings, creation, fork and
187
+ sync commands are unsupported external mutations. The shared command reader
188
+ preserves read-only repository commands, help and option values; explicit
189
+ GitHub repository lifecycle prose requires clarification as well.
190
+ - GitHub account SSH/GPG key additions and deletions are unsupported external
191
+ effects. Canonical `gh ssh-key`/`gh gpg-key` writes and explicit account-key
192
+ mutations require clarification. Listings, help, title/type values, local key
193
+ generation and product features retain their implementation meaning.
194
+ - Codespace creation, deletion, stopping, rebuilding and editing are unsupported
195
+ external effects. Canonical `gh codespace`/`gh cs` writes and imperative
196
+ Codespace lifecycle prose require clarification under omitted or covered PR
197
+ tags. The shared command reader preserves option values, read-only
198
+ list/status/view/logs/help, features and literal examples. No Codespace
199
+ action or prohibition tag is granted.
200
+ - GitHub Project creation, editing, deletion, copying and closing/reopening,
201
+ field/item writes, repository/team linking and template marking are unsupported
202
+ external mutations. Canonical `gh project` writes and explicit GitHub Project
203
+ mutation prose require clarification with omitted or covered PR tags; no
204
+ project action/prohibition authority is granted. The shared command reader
205
+ keeps option values atomic and preserves list/view/field-list/item-list/help,
206
+ ordinary local project work, product features and literal examples.
207
+ - Gist creation, editing, deletion and file renaming are unsupported external
208
+ mutations, including `gh gist create`/`new`, `edit`, `delete` and `rename`.
209
+ Both secret and public gists write account-owned content and require
210
+ clarification with omitted or covered PR tags; no gist action/prohibition
211
+ authority is granted. The shared command reader preserves option values,
212
+ read-only list/view/help and local gist cloning. Product viewers, feature
213
+ requests and literal examples retain their ordinary meaning.
214
+ - Repository label creation, editing, deletion and cloning are unsupported
215
+ external mutations. Canonical `gh label` writes and explicit GitHub/repository
216
+ label instructions require clarification under omitted or covered PR tags.
217
+ The shared quote-aware command reader preserves repository selectors and
218
+ option values; list/help, product features and literal examples keep their
219
+ ordinary meaning. Cloning labels writes the destination repository too.
220
+ No label action or prohibition tag is granted.
221
+ - Package registry administration is also a publication restriction. Write
222
+ subcommands of `npm access` (set/grant/revoke and legacy access/MFA setters),
223
+ `npm owner` (add/remove), and `npm dist-tag` (add/remove) require clarification.
224
+ List/get forms and help remain read-only. The quote-aware command argument
225
+ reader keeps option values and later commands from supplying a write verb.
226
+ - Outbound messages (email, Slack, chat and webhook instructions) are unsupported
227
+ external communications. Mutating `curl` calls, including explicit methods,
228
+ data/form/JSON bodies and file uploads, require clarification. Read-only
229
+ GET/HEAD/OPTIONS/TRACE requests and GET query options retain their meaning;
230
+ explicit methods and `--next` transfer boundaries are respected. Configuration
231
+ files or unresolved methods cannot silently choose a write. Header/body option
232
+ values, read-only help, local logging, product features and literal examples
233
+ do not become separate communication instructions. These screens never send
234
+ a message or issue a network request. Authenticated `gh api` writes use the
235
+ same HTTP conflict: explicit mutating methods and implicit POST from
236
+ field/input flags cannot use PR authority. Explicit GET overrides retain query
237
+ semantics, including field values; method, header, payload and output option
238
+ values stay atomic. Unknown modes require clarification. See the
239
+ [GitHub CLI API reference](https://cli.github.com/manual/gh_api).
240
+ - Hosted registry names and host destinations use the same publication
241
+ restriction: GitHub Packages/GHCR, GitLab registries, cloud artifact registries
242
+ and explicit registry hosts, including schemes, ports and artifact paths.
243
+ Detection, escalation and registry-removal checks share one destination
244
+ vocabulary. Quoted destinations of mutation instructions remain visible;
245
+ read-only pulls/downloads, feature infinitives and literal examples retain
246
+ their implementation meaning. Host formats follow [GitHub Container registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry),
247
+ [Google Artifact Registry](https://docs.cloud.google.com/artifact-registry/docs/docker/names),
248
+ [Amazon ECR](https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html)
249
+ and [Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal).
250
+ - Removing, deprecating or yanking registry packages retains the existing
251
+ publication conflict. This includes npm unpublish/deprecate, Cargo/RubyGems
252
+ yank and prose such as "remove the package from npm". No registry mutation
253
+ is authorized by PR delivery. Local dependency removal, feature descriptions
254
+ and literal examples keep their implementation meaning. Command semantics
255
+ follow [npm unpublish](https://docs.npmjs.com/cli/v11/commands/npm-unpublish/),
256
+ [npm deprecate](https://docs.npmjs.com/cli/v11/commands/npm-deprecate/) and
257
+ [Cargo yank](https://doc.rust-lang.org/cargo/commands/cargo-yank.html).
258
+ - Conversational owner decisions such as "check with me", "ask me" and "get
259
+ my go-ahead" are approval gates, including quoted decision objects. A plain
260
+ "go ahead and open a PR" retains its affirmative reading. Confirmation
261
+ dialogs, product features and literal examples keep their product meaning.
262
+ - Object-storage uploads and remote copies using AWS S3 cp/sync or gsutil
263
+ cp/rsync require clarification, as do moves that delete a remote source.
264
+ Prose uploads to S3 and other named object stores use the same restriction.
265
+ Operand direction, quoted option values and separate command boundaries are
266
+ preserved; download-only transfers, listing and unambiguous simulations retain
267
+ their read-only reading. Dynamic operands or unresolved option arity cannot
268
+ prove a download or dry run. See [AWS S3 transfer commands](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html).
269
+ - Imperative infrastructure writes, including Terraform/OpenTofu apply/destroy,
270
+ Helm install/upgrade/rollback/uninstall, kubectl resource mutations and Pulumi
271
+ up/destroy/refresh/import/cancel, retain
272
+ the existing deployment conflict. Plan/template/get commands and unambiguous
273
+ simulation flags (including Pulumi refresh --preview-only) keep their read-only
274
+ reading. Pulumi preview/history/stack output do not request an update.
275
+ Simulation flags cannot be
276
+ borrowed from another command or override a later mutating mode; quoted or
277
+ ambiguous option values keep a clarification. See [Terraform CLI](https://developer.hashicorp.com/terraform/cli/commands),
278
+ [Helm upgrade](https://docs.helm.sh/docs/helm/helm_upgrade/) and
279
+ [kubectl apply](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_apply/)
280
+ and [Pulumi up](https://www.pulumi.com/docs/iac/cli/commands/pulumi_up/).
281
+ - Force-push requirements (`--force`, `--force-with-lease`, short `-f`,
282
+ and direct force-push wording) conflict with the fixed task-branch safety
283
+ rule, even when the destination is `HEAD`. Known push-option values and
284
+ literal/product examples stay data; a PR tag cannot cover a force mode.
285
+ - Pushing tags, Git notes, all/mirrored refs, deleting remote refs or naming
286
+ another destination ref is a `direct_push` conflict. A base named before
287
+ the remote ("push main to origin", "push the main branch to origin") has the
288
+ same restriction, including literal selected-base names and reversed named
289
+ branch wording. Descriptive task/feature branches retain their ordinary
290
+ reading. A task-branch push such
291
+ as `git push -u origin HEAD` keeps the profile's normal reading. Product
292
+ commands and literal examples retain their feature meaning.
293
+ - Closing, reopening, editing, locking or changing the state of an existing
294
+ PR is a `pull_request_lifecycle` conflict, including imperative CLI commands
295
+ and quoted PR objects. The profile creates one new review PR; a planner's
296
+ `pull_request` tag cannot cover an existing-PR mutation or grant it authority.
297
+ Prose reviews, comments, feedback and replies use the same conflict as their
298
+ CLI equivalents; asking to open a new PR "for review" stays creation scope.
299
+ - Escalating terms (merge, including landing a PR, patch or change; deploy
300
+ including shipping, rollout, go-live and
301
+ "to production" wording, release, wait, local-only, a write aimed
302
+ at the base branch or at skipping the pull request such as "push directly
303
+ to main", "push to <the selected base>" with the profile's actual base name
304
+ or any explicitly direct branch write such as "commit straight to staging",
305
+ publish whenever it appears as a bare verb such as "Publish this package to
306
+ npm", "and publish the package" or "could you publish it", or as a package
307
+ or build submission to a registry or store such as "submit the package to
308
+ npm", "upload the build to the App Store" or "run gem push", rather than as a
309
+ noun modifier such as "a publish button" or a feature infinitive such as
310
+ "a command to publish docs", and approval whenever the statement waits on
311
+ someone's decision such as "wait for my approval", "get sign-off", "once
312
+ approved", "get my consent" or "get my authorization" (also "authorisation"
313
+ and "permission"), rather than a product feature such as "an approval
314
+ workflow", "a consent banner", "authorization middleware", "the approval
315
+ status column" or "a button to approve comments") or a
316
+ control word such as "do not", "until", "unless", "out of scope",
317
+ "exclude", "except", "other than", "but not", "disallowed", "isn't
318
+ allowed", "not permitted", "stop before", "pause before" or an owner
319
+ reservation such
320
+ as "I will open the PR myself", "let me open the PR" or "leave the PR to me"
321
+ next to any delivery
322
+ term
323
+ raise the same `q-delivery-<clause>` question when the planner left the
324
+ clause untagged.
325
+ - Dots inside registry and repository names stay in the statement, so
326
+ "Upload the package to hex.pm" and "Submit the package to crates.io" keep
327
+ their publication meaning. Quoted examples and feature infinitives retain
328
+ their product interpretation.
329
+ - A review prohibited outright ("do not send this for review", "no code
330
+ review", "reviews are forbidden") conflicts with pull-request delivery, which
331
+ exists to request review, so it keeps its clarification whatever the planner
332
+ tags. A review named as a condition ("do not merge until reviewed") or as
333
+ optional ("a review is not required") does not.
334
+ - Ordinary vocabulary ("Add a PR status badge") becomes a visible assumption in
335
+ the brief and in the review: the clause is treated as product behaviour and
336
+ the profile is unchanged.
337
+
338
+ The owner's reply to a delivery question is screened like any other clause,
339
+ and it closes the original conflict only when every fragment of the reply
340
+ carries a typed reading the profile covers; an anaphoric reply such as "do it
341
+ anyway" gets its own question instead.
342
+ That reading must be grounded in the reply's own explicit action or prohibited
343
+ effects. Empty `restrict` tags and invented actions or prohibitions cannot close
344
+ a delivery question. Restate the intended action (for example, "Open a pull
345
+ request for review; do not merge") or explicitly name the prohibited effects.
346
+ A reply that the planner reads as asking for an uncovered action, or that
347
+ carries escalating vocabulary the planner leaves untagged, raises the same
348
+ question for the reply itself. A reply that restates a prohibition the profile
349
+ already honours ("no deployment by this task") maps to criteria once the planner
350
+ tags it as a covered `restrict` reading; insisting on an uncovered action
351
+ cannot expand the profile. Prepare a new task with a different typed profile
352
+ instead.
353
+
354
+ ## The review
355
+
356
+ Every prepared revision returns one `review` object, pinned by `revision` and a
357
+ `digest` over the immutable request, fragments, answers, profile, harness,
358
+ route, draft hash, selected sources, repository snapshot and allowance:
359
+
360
+ - `request`: the original text, its hash and `immutable: true`.
361
+ - `requirementMap`: for each fragment, its source span, target (criteria,
362
+ assumption or question), any clarification, delivery tag, coverage, screen
363
+ result and conflict reason.
364
+ - `questions`, `assumptions` (planner assumptions plus screen assumptions) and
365
+ `draft` (path, hash, revision, text).
366
+ - `deliveryProfile`, `harness`, `allowance`, `modelRoute` and `cost`.
367
+ - `repositoryContext`, `readiness` and `approval`.
368
+
369
+ `task start --run` on the current revision records `approval` with that digest.
370
+ Answers clear readiness and approval, so a changed material input requires a
371
+ new review before the existing run path is callable. Preparation never authorizes
372
+ a new typed task, even if called with `run: true`.
373
+ An answered intake returns `ready` and needs its own `task start --run`. A saved
374
+ typed preparation flag without a review approval also requires that explicit
375
+ start. Tasks saved before typed profiles keep their documented
376
+ persisted authorization. A saved approval whose
377
+ digest no longer matches returns `TASK_CONFLICT` instead of dispatching. The
378
+ approved profile and approval travel into the implementation entry and are
379
+ checked again on reconciliation, so retries resume the same dispatch rather than
380
+ creating another.
381
+
382
+ The CLI prints the profile, planner route, cost status, repository snapshot
383
+ status and review summary; `--json` returns the complete object. The delivery
384
+ dispatcher and MCP views expose the same fields, and the bounded model summary
385
+ carries the profile action, completion, review revision and approval state.
386
+
387
+ ## Planner routes
388
+
389
+ | Route | When | Boundary |
390
+ | --- | --- | --- |
391
+ | `harness` | The selected harness declares a preparation boundary (Claude, Codex, OpenAI Agents) | Existing dispatch, session controls and usage recording. Cost is provider-reported or labelled unknown, never zero. |
392
+ | `model` | `intake_model` is configured and its credential is exported | One bounded text request per planning turn against the configured endpoint, with no tools, shell, web or repository access. |
393
+
394
+ Without `--route`, a harness with a preparation boundary is used; otherwise the
395
+ model route when it is enabled; otherwise the existing
396
+ `TASK_PREPARATION_BOUNDARY` refusal. `--route model` on a disabled route returns
397
+ `TASK_MODEL_ROUTE` with the configuration remedy before any effect. The task's
398
+ `harness` still names the implementation adapter in the brief's frontmatter.
399
+ Unavailable implementation adapters are refused with `EXECUTION_UNAVAILABLE`
400
+ before a new task or planning call, independently of the planner route. An
401
+ available adapter without isolated preparation may still use the model route.
402
+ Saved results remain replayable; a known availability failure before a fresh
403
+ turn can be explicitly resumed after restoring its implementation transport,
404
+ without replacing the pinned route or retrying an unsettled call.
405
+
406
+ ### Configuring the model route
407
+
408
+ ```json
409
+ "intake_model": {
410
+ "adapter": "openai-chat",
411
+ "model": "gpt-5-mini",
412
+ "endpoint": "https://api.openai.com/v1/chat/completions",
413
+ "credential_env": "OPENAI_API_KEY",
414
+ "budget": { "max_calls": 12, "max_input_tokens": 100000, "max_output_tokens": 16000,
415
+ "allowance_usd": 10, "input_usd_per_million": 0.25, "output_usd_per_million": 2 },
416
+ "timeout_ms": 120000
417
+ }
418
+ ```
419
+
420
+ `adapter` is `openai-chat` or `anthropic-messages`. The endpoint must be a
421
+ public HTTPS URL without credentials. The API key is read from the named
422
+ environment variable at call time and is never written to disk or returned by
423
+ any view. Ceilings are absolute: at most 12 calls per task, 100,000 input and
424
+ 16,000 output tokens per call, and a $10 allowance. Configuration can lower
425
+ them and cannot raise them. The task's runtime allowance bounds each call too:
426
+ the deadline is capped to the remaining runtime, and less than one second
427
+ remaining refuses before the request. Before each call the route bounds input tokens at
428
+ one token per UTF-8 byte of the complete serialized request (system message,
429
+ prompt and framing fields, plus a fixed margin for protocol tokens), the
430
+ byte-level fallback worst case, so emoji and rare scripts cannot slip under the
431
+ ceiling. It prices that bound plus
432
+ the output limit at the configured list prices, and refuses to start the call
433
+ when the result does not fit the remaining allowance. Reported usage above
434
+ either ceiling rejects the draft after the call, and usage that is negative,
435
+ fractional or non-numeric is treated as a malformed response rather than a
436
+ spend record. The response body is read under the same deadline as the request
437
+ and cancelled at one megabyte, so a stalled or oversized provider reply cannot
438
+ hang the task lock or buffer without bound. Cancelling a body, including an
439
+ error response's body, is awaited only until that deadline.
440
+
441
+ A missing pinned credential blocks before any model attempt or reservation is
442
+ created. Restore the named environment variable and explicitly resume the saved
443
+ task; known credential preflight recovery preserves accepted answers, the
444
+ original validation feedback and repair count. Only this known model pre-call failure
445
+ can rearm: submitted, unsettled or failed provider calls retain their ownership
446
+ and spend evidence and cannot purchase a replacement call through resume. Older markerless pre-call failures recover only when all saved calls are successful, settled and consumed with matching text-only receipts.
447
+
448
+ A task pins the complete route selection (adapter, model, endpoint, budget,
449
+ timeout and the credential variable name) when it is created. Later turns,
450
+ including answers and repairs, run against that pinned selection and the
451
+ review digest covers it, so changing or removing `intake_model` affects new
452
+ tasks only. The credential itself is still read from the environment on every
453
+ call; if the named variable is missing the turn stops with the export remedy. Reported usage replaces and releases the reservation, so the
454
+ implementation allowance that follows subtracts the reported spend rather than
455
+ the ceiling held during the call; missing usage leaves the reservation as the
456
+ spend bound with `spend.status: "unknown"`. These are
457
+ estimates from list prices, not provider invoices. Truncated, over-limit,
458
+ malformed or empty responses are rejected with a bounded error and count as a
459
+ call.
460
+
461
+ ## Repository context
462
+
463
+ Preparation collects a read-only snapshot of the selected checkout: the HEAD
464
+ revision and the path listing of that commit's tree, with ignored and private
465
+ paths removed (`.env`, credential, key and agent-configuration locations). The
466
+ checkout's in-tree `.gitignore` files decide ignores (local and global exclude
467
+ files do not), and they must match the recorded revision: an edited or
468
+ untracked `.gitignore` makes the snapshot unavailable with the files named,
469
+ instead of evaluating rules the recorded tree does not contain. A tree path the
470
+ index no longer holds (a staged deletion) is excluded as unverified and a
471
+ staged addition is never listed; the listing therefore always describes the
472
+ recorded revision. It is bounded to 400 paths and 16 KiB in the prompt; its
473
+ digest covers the complete approved listing at that revision. The revision,
474
+ the index and the ignore files are rechecked after the listings are read, so a concurrent
475
+ edit between the reads makes the snapshot unavailable instead of pairing the
476
+ recorded revision with another state. File contents reach the planner only through
477
+ explicitly selected `--context` documents, which are screened for credential
478
+ material. If the snapshot cannot be collected safely, the task records
479
+ `repositoryContext.status: "unavailable"` with the reason, and nothing is
480
+ guessed.
481
+
482
+ ## Historical tasks
483
+
484
+ Tasks saved before typed profiles existed keep their original interpretation.
485
+ Those with a Coordinator delivery contract keep its versioned validation; those
486
+ without one keep the workflow default. Inspection labels them `legacy` and they
487
+ can still be validated, started, resumed and controlled. A saved profile with an
488
+ unsupported version, or whose target repository, base or completion does not
489
+ match the task it is saved on, is readable through `inspect` and `list` and can
490
+ be exported, but every mutation returns `TASK_PROFILE_UNSUPPORTED` with the
491
+ remedy "prepare a new task". A record created with a typed profile (it carries
492
+ a planner route, a repository snapshot or a pinned model route) whose profile
493
+ is missing is treated the same way, never as a historical task. Such a record does not pin an idle namespace, so runtime migration
494
+ proceeds. New tasks always use the current typed profile; there is no fallback
495
+ to the free-text contract.
496
+
497
+ ## Agent Passport packaging
498
+
499
+ `lib/task_profile.mjs` and its portable host identity/declaration dependencies
500
+ are vendored together. They contain no Node transport or credential code. Its
501
+ completion conditions are checked against the coordinator schema by the focused
502
+ suite. `templates/plugin/task-intake-contract.json` pins the validator, every
503
+ transitive declaration file, the fixture corpus at
504
+ `templates/plugin/task-profile-requests.mjs` and the profile schema version.
505
+ All pinned files ship in the published package, so a consumer can run the exact
506
+ promised corpus. `npm run lint` fails when the pin is stale;
507
+ `npm run contract:intake -- --write` refreshes it after a reviewed change.
508
+ The plugin builder refuses to package when a pinned file differs and records
509
+ `intakeContract` in `runtime.json`. The
510
+ downstream `task-intake-vendor-sync` work in `aporthq/agent-passport` must pin
511
+ the published package, regenerate its bundled validator from these bytes and
512
+ run the same corpus before either repository's change is approved.
513
+
514
+ ## Verification
515
+
516
+ ```sh
517
+ npm test
518
+ npm run lint
519
+ npm run test:task-intake
520
+ git diff --check
521
+ ```
522
+
523
+ The focused suite covers the request corpus (direct, indirect, quoted,
524
+ contradictory, adversarial and ordinary feature wording), per-clause
525
+ traceability, typed-profile authorization, review invalidation, retry and
526
+ idempotency, historical fixtures, model-route call, token and allowance
527
+ boundaries, repository snapshots and a bounded-input timing check. It makes no
528
+ provider calls. Maximum-size timing fixtures use the UTF-8 byte counts of their
529
+ complete repeated units and command prefixes/suffixes, with assertions that
530
+ inputs fill but never exceed the accepted request allowance. The 50 CPU ms
531
+ screening budget is unchanged. Repeated statements share checks only within one
532
+ screen; every target-conflict occurrence and whole-request PR count is retained.
533
+ Passive landing checks first require a matching subject before inspecting
534
+ feature context; maximum-size probes include unrelated words followed by a real
535
+ instruction or a feature phrase. Timing failures identify the exact fixture.
536
+ No live qualification of generated-PRD quality or actual cost is
537
+ recorded here.