@uchibeke/gallivant 0.0.0-stage → 0.2.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 (168) hide show
  1. package/CONTRIBUTING.md +26 -0
  2. package/LICENSE +204 -0
  3. package/README.md +201 -2
  4. package/SECURITY.md +19 -0
  5. package/bin/gallivant.mjs +257 -0
  6. package/bin/package-plugin.mjs +16 -0
  7. package/bin/release-live.mjs +29 -0
  8. package/bin/release-package.mjs +23 -0
  9. package/bin/session-runtime.mjs +13 -0
  10. package/bin/task-delivery-state.mjs +12 -0
  11. package/docs/README.md +70 -0
  12. package/docs/agent-setup.md +214 -0
  13. package/docs/app.md +111 -0
  14. package/docs/architecture.md +265 -0
  15. package/docs/chains.md +102 -0
  16. package/docs/codex-cloud.md +284 -0
  17. package/docs/commands.md +620 -0
  18. package/docs/config.md +430 -0
  19. package/docs/execution.md +796 -0
  20. package/docs/guardrails.md +311 -0
  21. package/docs/install.md +338 -0
  22. package/docs/launch.md +42 -0
  23. package/docs/learning.md +369 -0
  24. package/docs/network-recovery.md +101 -0
  25. package/docs/openai-agents.md +457 -0
  26. package/docs/prd.md +246 -0
  27. package/docs/preparation.md +110 -0
  28. package/docs/release-qualification.md +196 -0
  29. package/docs/releasing.md +140 -0
  30. package/docs/runtime-environment.md +113 -0
  31. package/docs/task-delivery.md +476 -0
  32. package/docs/tasks.md +739 -0
  33. package/lib/aport.mjs +95 -0
  34. package/lib/attribution.mjs +95 -0
  35. package/lib/block.mjs +89 -0
  36. package/lib/capabilities.mjs +3 -0
  37. package/lib/chains.mjs +222 -0
  38. package/lib/codex_cloud.mjs +160 -0
  39. package/lib/codex_cloud_config.mjs +20 -0
  40. package/lib/codex_cloud_input.mjs +49 -0
  41. package/lib/codex_cloud_result.mjs +74 -0
  42. package/lib/commands/doctor.mjs +457 -0
  43. package/lib/commands/init.mjs +34 -0
  44. package/lib/commands/learn.mjs +54 -0
  45. package/lib/commands/new.mjs +47 -0
  46. package/lib/commands/ready.mjs +45 -0
  47. package/lib/commands/register.mjs +195 -0
  48. package/lib/commands/resume.mjs +224 -0
  49. package/lib/commands/serve.mjs +349 -0
  50. package/lib/commands/start.mjs +247 -0
  51. package/lib/commands/start_services.mjs +22 -0
  52. package/lib/commands/status.mjs +150 -0
  53. package/lib/commands/task.mjs +73 -0
  54. package/lib/commands/up.mjs +228 -0
  55. package/lib/commands/why.mjs +143 -0
  56. package/lib/config.mjs +461 -0
  57. package/lib/context.mjs +100 -0
  58. package/lib/control.mjs +202 -0
  59. package/lib/control_contract.mjs +16 -0
  60. package/lib/coordinator.mjs +19 -0
  61. package/lib/credential.mjs +161 -0
  62. package/lib/daemon.mjs +156 -0
  63. package/lib/deliverable.mjs +357 -0
  64. package/lib/events.mjs +261 -0
  65. package/lib/executable.mjs +31 -0
  66. package/lib/execution.mjs +631 -0
  67. package/lib/execution_contract.mjs +15 -0
  68. package/lib/execution_input.mjs +4 -0
  69. package/lib/execution_io.mjs +94 -0
  70. package/lib/execution_journal.mjs +69 -0
  71. package/lib/execution_preparation.mjs +29 -0
  72. package/lib/execution_result.mjs +266 -0
  73. package/lib/fs.mjs +133 -0
  74. package/lib/gh.mjs +289 -0
  75. package/lib/git.mjs +434 -0
  76. package/lib/harness.mjs +521 -0
  77. package/lib/harness_log.mjs +432 -0
  78. package/lib/hash.mjs +16 -0
  79. package/lib/identity.mjs +98 -0
  80. package/lib/learning.mjs +654 -0
  81. package/lib/learning_contract.mjs +2 -0
  82. package/lib/learning_data.mjs +135 -0
  83. package/lib/learning_input.mjs +111 -0
  84. package/lib/learning_qualification.mjs +67 -0
  85. package/lib/learning_signer.mjs +151 -0
  86. package/lib/learning_store.mjs +88 -0
  87. package/lib/log.mjs +53 -0
  88. package/lib/network_diagnostics.mjs +64 -0
  89. package/lib/network_recovery.mjs +42 -0
  90. package/lib/notify.mjs +58 -0
  91. package/lib/openai_agents.mjs +373 -0
  92. package/lib/openai_agents_config.mjs +14 -0
  93. package/lib/openai_agents_http.mjs +87 -0
  94. package/lib/openai_agents_input.mjs +15 -0
  95. package/lib/openai_agents_sources.mjs +112 -0
  96. package/lib/owner_delivery.mjs +443 -0
  97. package/lib/plugin_package.mjs +340 -0
  98. package/lib/prd.mjs +635 -0
  99. package/lib/preparation_agents.mjs +33 -0
  100. package/lib/preparation_codex.mjs +212 -0
  101. package/lib/probe.mjs +126 -0
  102. package/lib/prompt.mjs +238 -0
  103. package/lib/provider_health.mjs +55 -0
  104. package/lib/public-format.mjs +31 -0
  105. package/lib/public_output.mjs +49 -0
  106. package/lib/release_package.mjs +154 -0
  107. package/lib/repository_files.mjs +86 -0
  108. package/lib/run.mjs +385 -0
  109. package/lib/run_log.mjs +94 -0
  110. package/lib/runtime-environment.mjs +87 -0
  111. package/lib/runtime_dependencies.mjs +15 -0
  112. package/lib/runtime_node.mjs +107 -0
  113. package/lib/runtime_state.mjs +174 -0
  114. package/lib/sources/index.mjs +27 -0
  115. package/lib/sources/prd.mjs +50 -0
  116. package/lib/start_contract.mjs +37 -0
  117. package/lib/state.mjs +246 -0
  118. package/lib/task_allowance.mjs +31 -0
  119. package/lib/task_completion.mjs +61 -0
  120. package/lib/task_contract.mjs +450 -0
  121. package/lib/task_delivery.mjs +219 -0
  122. package/lib/task_delivery_cancel.mjs +34 -0
  123. package/lib/task_delivery_contract.mjs +61 -0
  124. package/lib/task_delivery_mcp.mjs +40 -0
  125. package/lib/task_delivery_store.mjs +43 -0
  126. package/lib/task_delivery_view.mjs +88 -0
  127. package/lib/task_environment.mjs +281 -0
  128. package/lib/task_prepare.mjs +224 -0
  129. package/lib/task_store.mjs +38 -0
  130. package/lib/tasks.mjs +513 -0
  131. package/lib/tick.mjs +1635 -0
  132. package/lib/time.mjs +36 -0
  133. package/lib/tracker/assets.mjs +43 -0
  134. package/lib/tracker/pages.mjs +320 -0
  135. package/lib/tracker/runs.mjs +102 -0
  136. package/lib/tracker/stream.mjs +74 -0
  137. package/lib/tracker_process.mjs +107 -0
  138. package/lib/verification_sandbox.mjs +181 -0
  139. package/node_modules/smol-toml/LICENSE +24 -0
  140. package/node_modules/smol-toml/README.md +605 -0
  141. package/node_modules/smol-toml/dist/date.d.ts +41 -0
  142. package/node_modules/smol-toml/dist/date.js +156 -0
  143. package/node_modules/smol-toml/dist/error.d.ts +37 -0
  144. package/node_modules/smol-toml/dist/error.js +67 -0
  145. package/node_modules/smol-toml/dist/extract.js +289 -0
  146. package/node_modules/smol-toml/dist/index.cjs +770 -0
  147. package/node_modules/smol-toml/dist/index.d.ts +44 -0
  148. package/node_modules/smol-toml/dist/index.js +34 -0
  149. package/node_modules/smol-toml/dist/parse.d.ts +73 -0
  150. package/node_modules/smol-toml/dist/parse.js +164 -0
  151. package/node_modules/smol-toml/dist/primitive.js +151 -0
  152. package/node_modules/smol-toml/dist/stringify.d.ts +32 -0
  153. package/node_modules/smol-toml/dist/stringify.js +219 -0
  154. package/node_modules/smol-toml/dist/struct.js +156 -0
  155. package/node_modules/smol-toml/dist/util.d.ts +43 -0
  156. package/node_modules/smol-toml/dist/util.js +57 -0
  157. package/node_modules/smol-toml/package.json +68 -0
  158. package/npm-shrinkwrap.json +1823 -0
  159. package/package.json +105 -4
  160. package/templates/claude-settings.json +18 -0
  161. package/templates/passport.gallivant.json +83 -0
  162. package/templates/plugin/bundled-runtime.json +26 -0
  163. package/templates/plugin/schemas/mcp.schema.json +120 -0
  164. package/templates/plugin/schemas/plugin.schema.json +65 -0
  165. package/templates/plugin/task-delivery.SKILL.md +60 -0
  166. package/templates/prd.md +32 -0
  167. package/templates/tracker/tracker.css +301 -0
  168. package/templates/tracker/tracker.js +721 -0
@@ -0,0 +1,443 @@
1
+ // A narrow, deterministic ownership partition for new natural-language tasks.
2
+ // Models may organize implementation criteria; they cannot reclassify owner text.
3
+ export const OWNER_DELIVERY_SECTION = "Coordinator delivery contract";
4
+ const fail = detail => { throw Object.assign(new Error(`Clarify the pull-request instruction: ${detail}. Gallivant can publish one review PR; selecting a completion target never authorizes a merge.`), { code: "TASK_DELIVERY_SCOPE" }); };
5
+ const negativeModal = String.raw`(?:(?:do|does|did|can|could|may|might|must|should|will|would|shall|need|is|are)\s+not|cannot|(?:don|doesn|didn|can|couldn|mayn|mightn|mustn|shouldn|won|wouldn|shan|needn|isn|aren)['’]t)`;
6
+ const prohibitionVerb = String.raw`forbid(?:s|den|ding)?|prohibit(?:s|ed|ing)?|ban(?:s|ned|ning)?|disallow(?:s|ed|ing)?|prevent(?:s|ed|ing)?|refrain|abstain|stop|pause|cancel|defer|delay`;
7
+ const negativeLead = String.raw`${negativeModal}|never|avoid|without|no|skip|neither|nor|${prohibitionVerb}`;
8
+ const directive = /\b(?:open|create|raise|submit)\s+(?:(?:one|a|an|the|single|another)\s+)?(?:new\s+)?(?:pull\s+request|pr)\b/ig;
9
+ const affirmative = /^(?:please\s+)?(?:open|create|raise|submit)\s+(?:(?:one|a|the|single)\s+)?(?:new\s+)?(?:pull\s+request|pr)(?:\s+for\s+(?:human\s+|code\s+)?review)?[.!]?$/i;
10
+ const publicationConnector = /(?:,\s*(?:and(?:\s+then)?|then)?|\band(?:\s+then)?|\bthen)\s*$/i;
11
+ const qualified = /\b(?:no|not|never|without|avoid|prevent|unless|if|when|after|before|until|whether|maybe|might|could|should|only)\b/i;
12
+ // A PR/template/title or published-article subject is not a publication request.
13
+ // Recognize actual actions and controls; supported affirmative clauses are still
14
+ // partitioned below, while unsupported/conditional forms require clarification.
15
+ // A workflow PR object can have arbitrary qualifiers ("any GitHub PR", "an
16
+ // experimental review PR"). Stop at references or another clause instead of
17
+ // enumerating adjectives or consuming "a report about the PR" as a PR action.
18
+ const publicationObjectBoundary = String.raw`and|or|nor|then|but|to|of|for|from|with|without|using|about|before|after|until|unless|when|if|while|because|by|on|in|at|into|onto|through|via|that|which|whose|where|referencing|mentioning|describing|concerning|regarding|reviewing|documenting|related`;
19
+ const publicationObjectLead = String.raw`(?:(?:(?:any|all|some|each|none|one)\s+of\s+)?(?!(?:${publicationObjectBoundary})\b)[\p{L}\p{N}_'’-]+\s+)*`;
20
+ const savedPublicationObjectLead = String.raw`(?:(?:one|a|an|the|single|another|new)\s+)*`;
21
+ const publicationActionPattern = current => new RegExp(String.raw`\b(?:open|create|raise|submit|send|prepare${current ? "|make|generate|produce|start|begin|file|issue|publish|deliver|draft" : ""})\s+${current ? publicationObjectLead : savedPublicationObjectLead}(?:pull[\s-]+requests?|prs?)\b|\b(?:start|begin)\s+publishing\b|\b(?:send|submit)\b[^.!;\n]*\bfor\s+(?:human\s+|code\s+)?review\b`, current ? "iu" : "i");
22
+ const publicationAction = publicationActionPattern(true), savedPublicationAction = publicationActionPattern(false);
23
+ // An imperative or coordinated action is a delivery direction. A feature's noun
24
+ // or infinitive ("a publish button", "a command to publish docs") is not.
25
+ const publishCommand = text => [...text.matchAll(/\bpublish\b/ig)].some(match =>
26
+ /(?:^|[.!;:\n,]|\b(?:and|then|do\s+not|don['’]t|never|only|must|should|shall|please|avoid|prevent|without|skip|want\s+you\s+to|need\s+you\s+to))\s*(?:(?:please|ever|again|now|[a-z]+ly)\s+)*$/i.test(text.slice(0, match.index)));
27
+ const withheldPublication = /\b(?:no|not|never|without|avoid|prevent|skip|cancel|stop|pause|withhold|defer|delay)\s+(?:(?:any|a|the|all|new)\s+)*(?:pull[\s-]+requests?|prs?|publication|publishing)\b/i;
28
+ const publicationRequirementPattern = current => {
29
+ const subject = current ? String.raw`(?:['’]s|['’])?\s+(?:(?:creation|opening|submission|generation|production|preparation|publication|publishing|delivery|drafting)\s+)?` : String.raw`\s+`;
30
+ return new RegExp(String.raw`\b(?:pull[\s-]+requests?|prs?|publication|publishing)${subject}(?:(?:${current ? negativeModal + "|" : ""}must|should|will|can|may|shall|is|are|be|not|never|only)\s+)*(?:created|opened|submitted|published|required|needed|allowed|permitted|forbidden${current ? "|prohibited|disallowed|banned" : ""}|requires?|needs?|awaits?)\b`, "i");
31
+ };
32
+ const publicationRequirement = publicationRequirementPattern(true), savedPublicationRequirement = publicationRequirementPattern(false);
33
+ const publicationCondition = /\b(?:pull[\s-]+requests?|prs?|publication|publishing)\s+(?:(?:is|are|will\s+be|must\s+be)\s+)?(?:only\s+)?(?:after|before|until|when|if|unless|pending|subject\s+to)\b/i;
34
+ function outsideApplicationPublications(text, saved = false) {
35
+ // Mask only the application verb, not its object or a following action. The
36
+ // negative-sequence check still sees the full text and any later Git control.
37
+ return text.replace(/\b(?:publish(?:ing)?|publication)\b/ig, (verb, offset) =>
38
+ technicalOperation(verb, text.slice(offset + verb.length), saved) && (saved || !mixedApplicationObjects(verb, text.slice(offset + verb.length))) ? " ".repeat(verb.length) : verb);
39
+ }
40
+ function publicationDirective(text, saved = false) {
41
+ const visible = outsideApplicationPublications(text, saved);
42
+ return publishCommand(visible) || [saved ? savedPublicationAction : publicationAction, withheldPublication].some(pattern => pattern.test(visible)) ||
43
+ !saved && publicationGerundProhibition.test(visible) ||
44
+ [saved ? savedPublicationRequirement : publicationRequirement, publicationCondition].some(pattern => pattern.test(text));
45
+ }
46
+ // Recognize directions about the workflow, not implementation nouns (a local
47
+ // store, review dashboard or diff viewer). This bounded grammar does not infer
48
+ // permission; unsupported explicit publication wording is refused separately.
49
+ // Direct and coordinated restrictions use the same bounded modal/subject forms.
50
+ const controlAdverb = String.raw`(?:under|in|at|by|for|on|without)\s+(?:(?:any|a|the|all|no|these|those|my|our|your|every)\s+)*[a-z][\w-]*(?:\s+whatsoever)?`;
51
+ const actionLeadFor = current => String.raw`(?:(?:you|we|I|it|they|the\s+agent)\s+)?(?:(?:should|must|may|can|could|would|will|shall|do|does|did)\s+)?(?:(?:you|we|I|it|they|the\s+agent)\s+)?(?:(?:not|ever|again|now|first${current ? `|please|even|once|just|only|${controlAdverb}` : ""}|[a-z]+ly)\s+)*(?:(?:run|running)\s+)?`;
52
+ const actionLead = actionLeadFor(true);
53
+ const actionVerb = (current = true) => String.raw`(?<git>git\s+)?(?<verb>push(?:es|ing)?|commit(?:s|ting)?|deliver(?:ing|y)?|publish(?:ing)?${current ? "|publication|creat(?:e|ing)|mak(?:e|ing)|generat(?:e|ing)|produc(?:e|ing)" : ""})\b`;
54
+ const coordination = String.raw`and|or|nor|as\s+well\s+as|along\s+with|together\s+with|in\s+addition\s+to|not\s+to\s+mention|besides|plus`;
55
+ const permissionLead = String.raw`(?:(?:be\s+)?(?:allowed|permitted|authorized|supposed|expected|required|able)\s+to\s+|(?:allow|permit)\s+)?`;
56
+ const prohibitionBridge = String.raw`(?:${publicationObjectLead}(?:from|against|to)\s+)?`;
57
+ const unfinishedControlPrefix = new RegExp(String.raw`\b(?:${negativeLead})\s+${prohibitionBridge}${permissionLead}(?:(?:any|a|the|all|more|new)\s+)*${actionLead}(?:to\s*)?$`, "iu");
58
+ const publicationGerund = String.raw`opening|creating|raising|submitting|sending|preparing|making|generating|producing|starting|beginning|filing|issuing|publishing|delivering|drafting`;
59
+ const publicationGerundProhibition = new RegExp(String.raw`\b(?:${negativeLead})\s+${prohibitionBridge}${permissionLead}${publicationObjectLead}(?:${publicationGerund})\s+(?:of\s+)?${publicationObjectLead}(?:pull[\s-]+requests?|prs?)\b`, "iu");
60
+ const controlledActionPattern = new RegExp(String.raw`\b(?:${negativeLead})\s+${prohibitionBridge}${permissionLead}(?:(?:any|a|the)\s+)?${actionLead}${actionVerb()}`, "igu");
61
+ const gitArtifactObject = tail => /^\s+(?:(?:any|a|an|the|this|that|these|those|new|another|additional|more)\s+)*(?:git\s+)?(?:commits?|branch(?:es)?)\b/i.test(outsideFeatureObjects(tail));
62
+ const coordinatedObjects = (tail, predicate) => [...tail.split(/[.!;\n]/)[0].matchAll(new RegExp(String.raw`\b(?:${coordination})\b|,`, "ig"))].some(match => predicate(tail.slice(match.index + match[0].length)));
63
+ const gitArtifactSequence = tail => gitArtifactObject(tail) || coordinatedObjects(tail, gitArtifactObject);
64
+ const objectHead = tail => tail.replace(/^\s+(?:of\s+)?/i, "").split(/[.!;\n,]|\b(?:and|or|nor|but|then|to|into|onto|on|via|through|using|from|in|at|for|with|twice|before|after|until|unless|when|if|once)\b/i)[0];
65
+ const workflowObject = (tail, saved = false) => {
66
+ const object = outsideFeatureObjects(objectHead(tail));
67
+ return /\b(?:git|github|remote|origin|upstream|pull[\s-]+requests?|prs?|commits?|branch(?:es)?|changes?|edits?|code|patch(?:es)?|work|implementation|release|build|package|results?|outputs?|anything|everything|nothing|it|them|this|that)\b/i.test(object) ||
68
+ !saved && /\b(?:solutions?|tasks?|deliverables?|artifacts?|fix(?:es)?|updates?|modifications?|revisions?|features?)\s*$/i.test(object);
69
+ };
70
+ const mixedApplicationObjects = (verb, tail) => {
71
+ // For prohibitions, retain unrecognized object phrases: an adjective before
72
+ // "changes" or "the PR" cannot hide a workflow object. Known later actions
73
+ // are checked independently, so a database commit is not a Git object here.
74
+ const next = [...tail.matchAll(coordinatedAction)][0];
75
+ return /^(?:publish|publication|deliver|merge)/i.test(verb) && coordinatedObjects(tail.slice(0, next?.index), workflowObject);
76
+ };
77
+ const savedMessageObject = /^\s+(?:of\s+)?(?:(?:the|a|an|these|those|duplicate|new|existing|outgoing|incoming|user|email|webhook)\s+)*(?:events?|messages?|notifications?|emails?|webhooks?)\b/i;
78
+ const savedApplicationDestination = /\b(?:to|into|onto|via|through|on)\s+(?:[\w-]+\s+)*(?:bus|queue|topic|broker|webhook|kafka|rabbitmq|nats|s3|gcs|r2|minio|storage|buckets?|sftp|ftp|scp)\b/i;
79
+ // Object names are open vocabulary. A shared explicit application destination
80
+ // supplies context; named workflow objects and bare temporal/adverbial controls
81
+ // never become application data just because another object follows them.
82
+ const applicationObject = (tail, saved = false) => {
83
+ const head = objectHead(tail).trim();
84
+ return !workflowObject(tail, saved) && /[\p{L}\p{N}]/u.test(head) &&
85
+ !/^(?:only|now|yet|today|tomorrow|later|again|online|pending|without|never|not|[a-z]+ly)$/i.test(head);
86
+ };
87
+ // Data-transfer actions are boundaries too, although they are not coordinator
88
+ // controls themselves. Require an object after the verb so plural nouns such
89
+ // as "logs to S3" and "archives to S3" can still share a destination.
90
+ const dataActionVerb = String.raw`(?:up|down)load(?:s|ing)?|cop(?:y|ies|ying)|mov(?:e|es|ing)|stor(?:e|es|ing)|sav(?:e|es|ing)|writ(?:e|es|ing)|send(?:s|ing)?|read(?:s|ing)?|fetch(?:es|ing)?|sync(?:s|ing)?|synchroniz(?:e|es|ing)|transfer(?:s|ring)?|persist(?:s|ing)?|record(?:s|ing)?|emit(?:s|ting)?|export(?:s|ing)?|import(?:s|ing)?|archiv(?:e|es|ing)|collect(?:s|ing)?|back(?:s|ing)?\s+up|log(?:s|ging)?|ship(?:s|ping)?`;
91
+ const dataActionPattern = new RegExp(String.raw`\b(?:${dataActionVerb})\b\s+(?!(?:to|into|onto|on|via|through|for|from|with|using|in|at|before|after|and|or|nor)\b)\S`, "ig");
92
+ const dataActionLead = new RegExp(String.raw`^${actionLead}(?:(?:${negativeLead})\s+${prohibitionBridge}${permissionLead}${actionLead})?$`, "iu");
93
+ const compoundDataObject = /^(?:log\s+entries|(?:download|upload)\s+(?:links?|urls?|paths?|names?|ids?|metadata))\b/i;
94
+ const dataAction = text => [...text.matchAll(dataActionPattern)].some(match => {
95
+ const lead = text.slice(0, match.index);
96
+ return dataActionLead.test(lead) || positiveActionLead.test(lead);
97
+ });
98
+ // An explicit destination permits an open application object, but borrowing a
99
+ // later object's destination requires positive noun-phrase evidence. Unknown
100
+ // words before an object may be verbs ("forward reports", "relay records");
101
+ // absence from the action vocabulary is not evidence that they are modifiers.
102
+ // Keep open noun heads with determiners, grammatical modifiers and established
103
+ // compounds. Ambiguous bare phrases can put a destination on each action.
104
+ // Suffixes are not evidence of a modifier: "give", "apply", "seal" and
105
+ // "seed" are imperative verbs. This bounded vocabulary is positive evidence,
106
+ // not a growing exclusion list of verbs. Unrecognized modifiers need an
107
+ // explicit destination; noun heads themselves remain open vocabulary.
108
+ const nounModifier = /^(?:new|existing|sent|tax|billing|daily|weekly|monthly|quarterly|annual|regional|archived|uploaded|downloaded|stored|copied|customized|cached|processed|normalized|validated|compressed|encrypted|sanitized|anonymized|redacted|generated|collected|aggregated)$/i;
109
+ const determinedModifier = /^(?:duplicate|parallel)$/i;
110
+ const bareDataNoun = /^(?:[\p{L}\p{N}-]+(?<!s|u)s|data|metadata|telemetry)$/iu;
111
+ const sharedApplicationObject = tail => {
112
+ if (!applicationObject(tail)) return false;
113
+ const head = objectHead(tail).trim();
114
+ const words = head.split(/\s+/);
115
+ const determined = /^(?:a|an|the|these|those|my|our|your|any|all|some)$/i.test(words[0]);
116
+ if (determined) words.shift();
117
+ // "log entries" and "download links" are both commands and noun compounds.
118
+ // Only an explicit determiner establishes the noun reading for shared scope.
119
+ const compound = words.join(" ");
120
+ if (determined && compound.match(compoundDataObject)?.[0] === compound) return true;
121
+ const noun = words.pop();
122
+ return Boolean(noun && (determined || bareDataNoun.test(noun)) && words.every(word => nounModifier.test(word) || determined && determinedModifier.test(word)));
123
+ };
124
+ function actionTail(tail, mergeOperands = false) {
125
+ // A destination belongs to this action, not a later coordinated action or
126
+ // incidental source/metadata. Keep shared destinations for noun lists.
127
+ const sentence = tail.split(/[.!;\n]|\b(?:before|after|while|when|until|unless|if|because)\b|\bfor\s+(?:(?:human|code)\s+)?review\b/i)[0];
128
+ for (const match of sentence.matchAll(new RegExp(String.raw`\b(?<conjunction>${coordination}|then|but)\s+|,\s*(?:(?:and|or|nor)\s+)?`, "ig"))) {
129
+ const initial = sentence.slice(0, match.index);
130
+ const remaining = sentence.slice(match.index + match[0].length);
131
+ const nextAction = [...sentence.slice(match.index).matchAll(coordinatedAction)][0];
132
+ if (nextAction?.index === 0 || dataAction(remaining)) return initial;
133
+ if (mergeOperands && !/^(?:then|but)$/i.test(match.groups.conjunction || "")) continue;
134
+ // Explicit Git targets still take precedence over application transports.
135
+ const destinationList = /\b(?:to|into|onto|on|via|through)\b/i.test(initial) && /^\s*(?:(?:the|a|an|public|private)\s+)*(?:git|github|remote|origin|upstream|repository|repo)\b/i.test(remaining);
136
+ // Purpose/condition text ("reports for review") ends the noun phrase and
137
+ // cannot borrow a later action's destination or repetition qualifier.
138
+ const initialObject = initial.split(new RegExp(String.raw`\b(?:${coordination})\b|,`, "i")).at(-1);
139
+ const nounList = initialObject.trim() === objectHead(initialObject).trim() && sharedApplicationObject(initialObject) && sharedApplicationObject(remaining);
140
+ if (/^(?:then|but)$/i.test(match.groups.conjunction || "") || !destinationList && !nounList) return initial;
141
+ }
142
+ return sentence;
143
+ }
144
+ function actionDestinations(tail) {
145
+ const current = actionTail(tail);
146
+ return [...current.matchAll(/\b(?:to|into|onto|via|through|on)\s+/ig)].map(match =>
147
+ current.slice(match.index + match[0].length).split(/\b(?:using|with|from|for|by|while|because)\b/i)[0]);
148
+ }
149
+ function applicationDelivery(tail, saved = false) {
150
+ // Preserve the earlier v1/v2 message exception only when validating saved
151
+ // spans; new requests must pass the contextual checks below.
152
+ if (saved && savedMessageObject.test(tail)) return true;
153
+ // Application context may come from the object, a transport, repeated delivery
154
+ // or nominal "delivery of ...". Unknown nouns alone never waive a workflow
155
+ // restriction ("do not publish the solution").
156
+ const scoped = saved ? tail : actionTail(tail);
157
+ const head = objectHead(scoped).replace(/^(?:(?:the|a|an|any|all|these|those|my|our|your|new|existing|duplicate)\s+)*/i, "").trim();
158
+ if (!head || /^(?:only|now|yet|today|tomorrow|later|again|online|pending|without|never|not|[a-z]+ly)\b/i.test(head) && !applicationObject(scoped, saved)) return false;
159
+ const destinations = actionDestinations(tail);
160
+ const gitDestination = destinations.some(destination => /\b(?:git(?:hub)?|remote|origin|upstream|repository|repo)\b/i.test(destination));
161
+ const applicationDestination = destinations.some(destination => /\b(?:bus|queue|topic|broker|webhook|kafka|rabbitmq|nats|s3|gcs|r2|minio|storage|buckets?|sftp|ftp|scp)\b/i.test(destination)) ||
162
+ saved && savedApplicationDestination.test(tail);
163
+ const applicationContext = /^\s+of\s+/i.test(tail) || /\b(?:events?|messages?|notifications?|emails?|webhooks?)\b/i.test(head) ||
164
+ /\b(?:twice|duplicates?|multiple\s+times|more\s+than\s+once)\b/i.test(scoped);
165
+ const workflow = workflowObject(scoped, saved);
166
+ return !gitDestination && (applicationDestination && (saved || !workflow) || applicationContext && !workflow);
167
+ }
168
+ function technicalOperation(verb, tail, saved = false) {
169
+ if (/^commit/i.test(verb)) return /^\s+(?:(?:the|a|this|that)\s+)?(?:(?:database|sql|db)\s+)?transactions?\b/i.test(tail);
170
+ if (/^push/i.test(verb)) return /^\s+(?:(?:the|a|an|this|that|duplicate|new|existing)\s+)*(?:items?|elements?|values?|records?|messages?|entries)\s+(?:onto|into|to)\s+(?:(?:the|a|an|this|that)\s+)?(?:queue|stack|array|list)\b/i.test(tail);
171
+ if (/^(?:publish|publication|deliver)/i.test(verb)) return applicationDelivery(tail, saved);
172
+ if (/^(?:creat|mak|generat|produc)/i.test(verb)) return !gitArtifactSequence(tail);
173
+ if (/^merge/i.test(verb)) {
174
+ const head = objectHead(tail).trim().replace(/^(?:(?:a|an|the|any|all|some|more|only|just|please|my|our|your)(?:\s+|$))+/i, "").replace(/\s+please$/i, "");
175
+ const operands = actionTail(tail, true);
176
+ const targets = [...operands.matchAll(/\b(?:from|to|into|onto|on|via|through|using|in|at|for|with|against)\s+/ig)].flatMap(match => operands.slice(match.index + match[0].length).split(new RegExp(String.raw`\b(?:${coordination})\b|,`, "i")).map(objectHead));
177
+ const data = bareDataNoun.test(head.split(/\s+/).at(-1) || "") || /\b(?:arrays?|objects?|records?|contacts?|rows?|accounts?|files?|invoices?|streams?|maps?|dictionar(?:y|ies)|payloads?|configurations?|tables?|datasets?|database|json|sql)\b/i.test(head);
178
+ const headCountData = /^head\s+counts?$/i.test(head) && /\b(?:to|into)\s+(?:the\s+)?database\b/i.test(actionTail(tail));
179
+ return Boolean(head) && data && (headCountData || !gitMergeReference(head)) && !targets.some(target => workflowObject(target) || gitMergeReference(target)) &&
180
+ !/^(?:now|yet|today|tomorrow|later|again|online|pending|without|never|not)(?:\s|$)/i.test(head) && applicationObject(tail) && applicationObject(` ${head}`) && !mixedApplicationObjects(verb, tail);
181
+ }
182
+ return false;
183
+ }
184
+ const mergeOperandText = value => value.replace(/[()[\]{}<>]/g, " ").trim().replace(/\s+/g, " ");
185
+ function gitMergeReference(value) {
186
+ const ref = mergeOperandText(mergeOperandText(value).replace(/^(?:(?:a|an|the|also|another|any|all|some|both|either|neither|other|more|further|extra|additional|remaining|selected|specified|following|this|that|these|those|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety|hundred|thousand|\d+)\s+)+/i, ""));
187
+ const candidates = ref.split(/\s+/).map(mergeOperandText).filter(token => !/^(?:\/|\.{1,2}\/|~\/|[a-z][a-z0-9+.-]*:\/\/|[a-z]:[\\/])/i.test(token)).join(" ");
188
+ const untyped = candidates.replace(/\b(?:objectid|uuid|checksum|digest)(?:\s*[:=]\s*|\s+)[a-f0-9-]{4,128}(?=$|[^\p{L}\p{N}_-])/giu, " ");
189
+ if (/[\/~^@]/.test(untyped) || /(?:^|[^\p{L}\p{N}_-])[a-f0-9]{7,64}(?=$|[^\p{L}\p{N}_-])/iu.test(untyped) ||
190
+ /\b(?:sha(?:\p{Pd}?(?:1|256))?|oid|hash|changeset)[\s:=\p{Pd}]+[a-f0-9]{4,64}(?=$|[^\p{L}\p{N}_-])/iu.test(untyped)) return true;
191
+ const qualified = /^(?:(?:git|remote|local)\s+)*(?:branches?|tags?|revisions?|commits?|refs?|references?|remotes?|heads?)(?:[\s:=\p{Pd}]+(.*))?$/iu.exec(ref);
192
+ if (qualified) {
193
+ const operand = (qualified[1] || "").trim();
194
+ if (!operand && /^(?:tags|references)$/i.test(ref)) return false;
195
+ // Typed application compounds describe data, while a single ref token or
196
+ // revision expression after a qualifier identifies the Git operand.
197
+ if (/^(?:counts?|metadata|data|details?|names?|labels?|statistics|metrics|records?|objects?|payloads?|schemas?|models?|tables?|fields?)\s+(?:data|metadata|records?|objects?|tables?)$/i.test(operand)) return false;
198
+ return true;
199
+ }
200
+ // Git refs cannot contain whitespace. Named data fields such as "the main
201
+ // table" remain data, while bare branch names, qualified refs and revisions
202
+ // cannot supply application evidence.
203
+ return !/\s/.test(ref) && /^(?:main|master|develop|development|dev|stage|staging|prod|production|release|trunk|head|fetch_head|orig_head|merge_head|v\d+)$/i.test(ref);
204
+ }
205
+ // Negation also spans "nor", including technical actions before a Git action.
206
+ // A technical exception for one verb must never exempt the rest of the sequence.
207
+ const coordinatedPattern = current => new RegExp(String.raw`(?:\b(?:${current ? coordination : "and|or|nor"}|then|but)\s+|,\s*(?:(?:and|or|nor|then|but)\s+)?)${actionLeadFor(current)}${actionVerb(current)}`, "ig");
208
+ const coordinatedAction = coordinatedPattern(true), savedCoordinatedAction = coordinatedPattern(false);
209
+ const workflowAction = (verb, tail, git, saved = false) => Boolean(git) || !technicalOperation(verb, tail, saved) || !saved && mixedApplicationObjects(verb, tail);
210
+ function workflowSequence(verb, tail, git, saved = false) {
211
+ const rest = tail.split(/[.!;\n]/)[0];
212
+ return workflowAction(verb, rest, git, saved) || [...rest.matchAll(saved ? savedCoordinatedAction : coordinatedAction)].some(match => workflowAction(match.groups.verb, rest.slice(match.index + match[0].length), match.groups.git, saved));
213
+ }
214
+ const negativeClause = new RegExp(String.raw`\b(?:${negativeLead})\b`, "ig");
215
+ const controlledAction = text => [...text.matchAll(controlledActionPattern)].some(match => workflowSequence(match.groups.verb, text.slice(match.index + match[0].length), match.groups.git)) ||
216
+ [...text.matchAll(negativeClause)].some(negative => {
217
+ // An unknown first application verb ("do not log keys or commit changes")
218
+ // must not erase a later coordinated workflow restriction. Conditions and
219
+ // independent statements end this scope; literals are already masked.
220
+ const clause = text.slice(negative.index + negative[0].length).split(/[.!;\n]|\b(?:before|after|while|when|until|unless|if|because)\b/i)[0];
221
+ return [...clause.matchAll(coordinatedAction)].some(match => workflowAction(match.groups.verb, clause.slice(match.index + match[0].length), match.groups.git));
222
+ });
223
+ const controlledWorkPattern = /\b(?:stop|pause|cancel|hold|withhold|delay|defer|skip)\s+(?:(?:the|this|these|my|our|your|all|any)\s+)?(?<verb>changes?|edits?|work|code|implementation|tasks?|runs?|push(?:es|ing)?|commit(?:s|ting)?|delivery|publication|review|it|them|everything)\b|\b(?:stop|pause|cancel)\s*(?:[.!;]|$)/ig;
224
+ const controlledWork = text => [...text.matchAll(controlledWorkPattern)].some(match => workflowSequence(match.groups.verb, text.slice(match.index + match[0].length)));
225
+ // Affirmative workflow merges are never delegated to implementation. Negative
226
+ // merge constraints and product features remain requirements; literals are masked
227
+ // by the caller. This check applies only to new requests/answers, not saved spans.
228
+ const mergeAction = /\b(?:(?:(?:enable|allow|turn\s+on|set)\s+(?:the\s+)?|set\s+(?:(?:this|that|the|a)\s+)?(?:pr|pull\s+request)\s+to\s+)(?<auto>auto[-\s]?merge|automatic\s+merging)|(?<git>git\s+)?(?<verb>(?:(?:auto|squash|rebase)[-\s]?)?merge))\b/ig;
229
+ // Positive auxiliary sequences share one grammar ("will need to", "will have
230
+ // to", "will be required to"). Negatives are intentionally absent.
231
+ const positiveActionLead = /(?:^|[.!;:\n,]|\b(?:and|then))\s*(?:please\s+)?(?:(?:you|we|I|the\s+agent)\s+)?(?:(?:must|should|can|could|may|might|will|would|shall|needs?|wants?|requires?|requests?|expects?|instructs?|directs?|orders?|tells?|asks?|demands?|mandates?|authoriz(?:e|es)|allows?|permits?|assigns?|tasks?|make\s+sure|makes?|gets?|let|let['’]s|like|have|has|be|are|is|required|expected|supposed|going|got|you|us|me|them|the\s+(?:agent|assistant|worker)|to|that|please)\s+)*(?:(?:automatically|now|first|squash|rebase)\s+)*(?:(?:run|running)\s+)?$/i;
232
+ const mergeDirective = text => [...text.matchAll(mergeAction)].some(match => positiveActionLead.test(text.slice(0, match.index)) &&
233
+ (match.groups.git || match.groups.auto || !technicalOperation("merge", text.slice(match.index + match[0].length))));
234
+ const passiveGitProhibition = new RegExp(String.raw`\b(?:commits?|committing|branch(?:es|ing)?)(?:\s+(?:creation|generation|production))?\s+(?:(?:${negativeModal}|(?:(?:must|should|shall|may|can|will|is|are)\s+)?(?:not|never))\s+(?:to\s+)?(?:be\s+)?(?:created|generated|made|produced|written|allowed|permitted|happen|occur)|(?:(?:is|are|must\s+be|should\s+be)\s+)?(?:forbidden|prohibited|disallowed|banned))\b`, "i");
235
+ const gitArtifactProhibition = text => passiveGitProhibition.test(text) || [...text.matchAll(/\b(?:no|without)\b/ig)].some(match => gitArtifactObject(text.slice(match.index + match[0].length)));
236
+ const localWork = /\b(?:keep|leave|save|retain)\s+(?:(?:the|this|these|those|my|our|all)\s+)?(?:changes?|edits?|work|code|results?|outputs?|files?|branches|branch|it|them|this|these|those|everything)\s+(?:(?:only|strictly)\s+)?(?:local(?:ly|-only)?|unpublished|uncommitted|unpushed)\b|\b(?:keep|leave)\s+(?:only\s+)?local(?:ly)?(?=\s*(?:[.!;]|$))|\b(?:work|operate)\s+(?:only\s+locally|locally\s+only)\b/i;
237
+ const artifactOnly = /\b(?:return|show|provide|give|send)\s+(?:(?:me|us)\s+)?(?:(?:only|just)\s+(?:(?:a|the)\s+)?(?:patch|diff)|(?:(?:a|the)\s+)?(?:patch|diff)\s+only)\b|\b(?:only|just)\s+(?:return|show|provide|give|send)\s+(?:(?:me|us)\s+)?(?:(?:a|the)\s+)?(?:patch|diff)\b/i;
238
+ const awaitReview = /\b(?:wait(?:\s+for)?|await)\s+(?:(?:my|our|your|the|an?|explicit|human|owner|user)\s+){0,3}(?:review|approval|permission|confirmation|consent)\b/i;
239
+ const heldForApproval = /\b(?:hold|keep|leave)\s+(?:(?:the|this|these|my|our)\s+)?(?:changes?|edits?|work|code|it|them)\s+(?:for|until|pending)\s+(?:(?:my|our|the|explicit|human|owner)\s+){0,3}(?:approval|permission|confirmation|consent)\b/i;
240
+ // New-request/answer validation must not reinterpret already accepted spans.
241
+ const publicationPreference = /\b(?:no|not|don['’]t|doesn['’]t|didn['’]t|never|without|avoid|prevent|skip)\s+(?:want|need|require|request)\s+(?:for\s+)?(?:(?:any|a|the|all|new)\s+)*(?:pull[\s-]+requests?|prs?|publication|publishing)\b/i;
242
+ const deliveryControl = text => controlledAction(text) || controlledWork(text) || mergeDirective(text) || gitArtifactProhibition(text) ||
243
+ publicationPreference.test(outsideApplicationPublications(text)) || [localWork, artifactOnly, awaitReview, heldForApproval].some(pattern => pattern.test(text));
244
+ // Owner authorization prerequisites affect the whole task. Technical conditions
245
+ // (if a name is empty; commit a database transaction after validation) remain
246
+ // implementation scope. Conditional/negative publication clauses themselves are
247
+ // independently rejected by `qualified` above; punctuation cannot erase them.
248
+ const requestedApproval = /\b(?:ask|obtain|request|require)\s+(?:for\s+)?(?:(?:the|explicit|further|manual)\s+)*(?:my|our|your|owner)\s+(?:(?:explicit|further|manual)\s+)*(?:review|approval|permission|confirmation|consent|go-ahead)\b/i;
249
+ const conditionedApproval = /\b(?:before|after|until|unless|pending|once|when|if|provided|subject\s+to|conditioned\s+on)\b[^.!;\n]*\b(?:(?:I|we|you|the\s+owner|a\s+human|the\s+reviewer)\s+(?:approve|confirm|consent|permit|agree|review|authorize)|(?:my|our|your|owner|human|manual|explicit)\s+(?:review|approval|permission|confirmation|consent))\b/i;
250
+ const requiredApproval = /\b(?:my|our|your|owner)\s+(?:approval|permission|confirmation|consent)\s+(?:(?:is|are|must\s+be)\s+)?(?:required|needed|pending|first)\b|\bask\s+(?:me|us|the\s+owner)\s+(?:first|before)\b|(?:^|[.!;\n])\s*(?:approval|permission|confirmation|consent)\s+(?:(?:is|are|must\s+be)\s+)?(?:required|needed|pending|first)\s*(?:[.!;]|$)/i;
251
+ const standalonePrerequisite = /(?:^|[.!;\n])\s*(?:only\s+(?:after|before|until|when|if)|not\s+until)\b/i;
252
+ const approvalAction = /\b(?:approval|permission|confirmation|consent)\b[^.!;\n]*?\b(?:before|after|until|for|to)\s+(?:we\s+|you\s+)?(?:(?:run|running)\s+)?(?<git>git\s+)?(?<verb>proceed(?:ing)?|continu(?:e|ing)|publish(?:ing)?|deliver(?:ing)?|push(?:ing)?|commit(?:ting)?|open(?:ing)?|creat(?:e|ing)|submit(?:ting)?)\b/ig;
253
+ const approvalForWorkflow = (text, saved = false) => [...text.matchAll(approvalAction)].some(match => {
254
+ const { verb, git } = match.groups, tail = text.slice(match.index + match[0].length);
255
+ // These new controls apply at intake. Saved v1/v2 contracts retain the action
256
+ // grammar under which their source spans were originally approved.
257
+ if (saved && /^deliver/i.test(verb)) return false;
258
+ if (/^(?:push|commit|publish|deliver)/i.test(verb)) return workflowSequence(verb, tail, git, saved);
259
+ if (/^(?:open|creat|submit)/i.test(verb)) return /^\s+(?:(?:a|the|one)\s+)?(?:pr|pull\s+request)\b/i.test(tail);
260
+ return true;
261
+ });
262
+ const ownerPrerequisite = (text, saved = false) => approvalForWorkflow(text, saved) || [requestedApproval, conditionedApproval, requiredApproval, standalonePrerequisite].some(pattern => pattern.test(text));
263
+ const stable = value => JSON.stringify(value, (_, x) => x && typeof x === "object" && !Array.isArray(x) ? Object.fromEntries(Object.keys(x).sort().map(k => [k, x[k]])) : x);
264
+
265
+ // Keep offsets in JavaScript UTF-16 units, explicitly. Mask literals before
266
+ // locating punctuation or directives: examples/code are not publication authority.
267
+ const apostrophe = (source, index) => /[\p{L}\p{N}]/u.test(source[index - 1] || "") && /[\p{L}\p{N}]/u.test(source[index + 1] || "");
268
+ const leadingElision = (source, index) => source[index] === "'" && !/[\p{L}\p{N}]/u.test(source[index - 1] || "") && /^(?:\d{2}s|tis|twas|twere|cause|em|til|till|round|bout)\b/i.test(source.slice(index + 1));
269
+ function literalClose(source, start, close, saved = false) {
270
+ for (let i = start + 1; i < source.length; i++) {
271
+ if (source[i] === "\\") { i++; continue; }
272
+ if (!source.startsWith(close, i) || (["'", "’"].includes(close) && apostrophe(source, i)) || !saved && leadingElision(source, i)) continue;
273
+ if (close[0] === "`" && (source[i - 1] === "`" || source[i + close.length] === "`")) continue;
274
+ return i;
275
+ }
276
+ return -1;
277
+ }
278
+ function outsideLiterals(source, saved = false, elisionNonOpeners = true) {
279
+ let out = "";
280
+ for (let i = 0; i < source.length; i++) {
281
+ const c = source[i], smart = c === "“" || c === "‘";
282
+ const opener = smart || c === '"' || c === "`" || c === "'" && !/[\p{L}\p{N}]/u.test(source[i - 1] || "");
283
+ if (!opener) { out += c; continue; }
284
+ if (!saved && elisionNonOpeners && leadingElision(source, i)) { out += c; continue; }
285
+ const close = smart ? c === "“" ? "”" : "’" : c === "`" ? source.slice(i).match(/^`+/)[0] : c;
286
+ const end = literalClose(source, i + (c === "`" ? close.length - 1 : 0), close, saved);
287
+ if (end === -1) {
288
+ // Historical v1/v2 ownership used an open ASCII literal through EOF.
289
+ // Preserve those approved spans, but never create or answer a new task
290
+ // with a malformed literal that could hide a delivery restriction.
291
+ if (smart || !saved && leadingElision(source, i)) { out += c; continue; }
292
+ if (!saved) fail("literal examples need matching closing delimiters");
293
+ out += source.slice(i).replace(/[^\n]/g, " "); break;
294
+ }
295
+ const length = end + close.length - i;
296
+ out += source.slice(i, i + length).replace(/[^\n]/g, " "); i += length - 1;
297
+ }
298
+ return out;
299
+ }
300
+ // Only the workflow modifier in an explicit software/document object is ignored by
301
+ // workflow recognition. Preserve its suffix and every other action/condition.
302
+ // An actual PR/Git object is not automatically a fixture. These ambiguous data
303
+ // suffixes need an explicit synthetic/representation qualifier in the same noun
304
+ // phrase; clause boundaries prevent a qualifier from masking a later real action.
305
+ const localObjectLead = publicationObjectLead.replaceAll(String.raw`\s`, String.raw`[ \t]`);
306
+ const representedPrObject = new RegExp(String.raw`\b(?:mock|synthetic|fake|dummy|stub|normalized|serialized|deserialized|json|in-memory|sample|example|test|fixture)[ \t]+${localObjectLead}(?<modifier>(?:pull[ \t-]+requests?|prs?)(?:['’]s|['’])?)(?=[ \t-]+(?:objects?|records?|payloads?|schemas?|models?|representations?)\b)`, "igu");
307
+ const featureObjectsPattern = current => {
308
+ const space = current ? String.raw`[ \t-]+` : String.raw`[\s-]+`;
309
+ const modifierSpace = current ? String.raw`[ \t-]?` : String.raw`[-\s]?`;
310
+ return new RegExp(String.raw`\b(?:pull${space}requests?|prs?|commits?|branch(?:es)?|(?:(?:auto|squash|rebase)${modifierSpace})?merge)(?:['’]s|['’])?(?=${space}(?:(?:details?|review|status)${space})?(?:${current ? "fixtures?|mocks?|stubs?|examples?|samples?|" : ""}templates?|dialogs?|forms?|components?|views?|pages?|panels?|dashboards?|buttons?|badges?|widgets?|selectors?|selection|messages?|reports?|summar(?:y|ies)|descriptions?|titles?|bod(?:y|ies)|labels?|comments?|discussions?|reviews?|feedback|diffs?|checks?|status(?:es)?|states?|names?|ids?|hashes?|shas?|metadata|history|counts?|links?|urls?|references?)\b)`, "ig");
311
+ };
312
+ const featureObjects = featureObjectsPattern(true), savedFeatureObjects = featureObjectsPattern(false);
313
+ function outsideFeatureObjects(text, saved = false) {
314
+ const visible = saved ? text : text.replace(representedPrObject, (match, modifier) => match.slice(0, -modifier.length) + " ".repeat(modifier.length));
315
+ return visible.replace(saved ? savedFeatureObjects : featureObjects, match => " ".repeat(match.length));
316
+ }
317
+
318
+ export function ownerDelivery(source) {
319
+ const value = partitionOwnerDelivery(source, 5);
320
+ const masked = outsideFeatureObjects(outsideLiterals(source));
321
+ if (publicationGerundProhibition.test(masked)) fail("withheld publication needs an explicit supported delivery contract");
322
+ const outsidePublication = (value?.spans || []).filter(span => span.owner === "coordinator").reduce((text, span) => text.slice(0, span.start) + " ".repeat(span.end - span.start) + text.slice(span.end), masked);
323
+ if (deliveryControl(outsidePublication) || ownerPrerequisite(outsidePublication)) fail("withheld, local-only or approval-dependent delivery needs an explicit supported delivery contract");
324
+ return value;
325
+ }
326
+
327
+ // Saved versions retain their historical literal scanner. Version four treats
328
+ // elisions as non-openers; version five bounds feature objects to the same line.
329
+ function partitionOwnerDelivery(source, version, saved = false) {
330
+ if (typeof source !== "string" || !source.trim()) fail("a nonempty owner request is required");
331
+ const masked = outsideFeatureObjects(outsideLiterals(source, saved && version < 3, !saved || version >= 4), saved && version < 5), deliveries = [];
332
+ let from = 0;
333
+ const statements = [];
334
+ for (let i = 0; i < masked.length; i++) if (masked[i] === ";" || masked[i] === "\n" || /[.!]/.test(masked[i]) && /\s|$/.test(masked[i + 1] || "")) {
335
+ statements.push([from, i + 1]); from = i + 1;
336
+ }
337
+ statements.push([from, source.length]);
338
+ let previousStatement = "";
339
+ for (const [start, end] of statements) {
340
+ const visible = masked.slice(start, end), matches = [...visible.matchAll(directive)];
341
+ const inherited = /:\s*$/.test(previousStatement) ? previousStatement : "";
342
+ const inheritedControl = !saved && unfinishedControlPrefix.test(previousStatement);
343
+ if (visible.trim()) previousStatement = visible;
344
+ if (!matches.length) {
345
+ // Unrecognized affirmative wording is no safer to delegate than a
346
+ // restriction. Require clarification rather than making a provider attest
347
+ // publication that only the coordinator can perform.
348
+ if (publicationDirective(visible, saved)) fail("publication wording needs an explicit supported delivery contract");
349
+ continue;
350
+ }
351
+ // Negation/conditions can scope a later coordinated verb. Do not guess that
352
+ // a comma or 'and' ended that scope ("do not merge, open a PR").
353
+ if (qualified.test(visible) || qualified.test(inherited) || inheritedControl || matches.length !== 1) fail("negated, conditional or multiple publication clauses need an explicit supported delivery goal");
354
+ const match = matches[0], prefix = visible.slice(0, match.index);
355
+ const connector = prefix.match(publicationConnector);
356
+ const boundary = /^\s*(?:please\s+)?$/i.test(prefix) || connector;
357
+ if (!boundary) fail("publication must be an unambiguous affirmative clause, not an example or an instruction to describe it");
358
+ // Courtesy text and punctuation belong to this clause too. Leaving a final
359
+ // period behind would turn it into an authoritative implementation fragment.
360
+ let a = /^\s*please\s+$/i.test(prefix) ? start + prefix.search(/\S/) : start + match.index, b = end;
361
+ while (b > a && /\s/.test(source[b - 1])) b--;
362
+ const clause = source.slice(a, b).replace(/;$/, "");
363
+ if (!affirmative.test(clause)) fail("publication has unsupported qualifiers or is mixed with another action");
364
+ // A recognized conjunction/delimiter belongs to the following publication
365
+ // clause, not an incomplete provider requirement. Validate that clause first;
366
+ // moving its boundary must not discard a condition or approve another action.
367
+ if (version >= 2) {
368
+ // A connector split over statement boundaries is outside this grammar.
369
+ // Do not leave it in provider criteria or consume another statement.
370
+ if (publicationConnector.test(masked.slice(0, start))) fail("publication connectors must stay in the same statement as the publication clause");
371
+ if (connector) {
372
+ const rawConnector = source.slice(start, start + match.index).match(publicationConnector);
373
+ if (rawConnector?.index !== connector.index) fail("publication connectors cannot contain quoted implementation text");
374
+ a = start + connector.index;
375
+ while (a > start && /\s/.test(source[a - 1])) a--;
376
+ }
377
+ }
378
+ deliveries.push({ start: a, end: b, owner: "coordinator", kind: "pull_request" });
379
+ }
380
+ if (!deliveries.length) return null;
381
+ const outsidePublication = deliveries.reduce((text, span) => text.slice(0, span.start) + " ".repeat(span.end - span.start) + text.slice(span.end), masked);
382
+ if (ownerPrerequisite(outsidePublication, saved)) fail("publication prerequisites require an explicit clarified task contract");
383
+ if (deliveries.length !== 1) fail("one task can publish only one review PR");
384
+ const spans = [], publication = deliveries[0];
385
+ if (publication.start > 0) spans.push({ start: 0, end: publication.start, owner: "implementation" });
386
+ spans.push(publication);
387
+ if (publication.end < source.length) spans.push({ start: publication.end, end: source.length, owner: "implementation" });
388
+ // Negative feature requirements are meaningful work. Exclude only bare
389
+ // delivery cautions/scope labels, preserving the historical technical exception.
390
+ const implementation = spans.filter(s => s.owner === "implementation").map(s => source.slice(s.start, s.end)).join(" ")
391
+ .split(/[;.!\n]/)
392
+ .filter(s => !/^\s*(?:(?:please|and|then)\s+)*(?:scope\s*:|(?:do\s+not|never|no)\s+(?:merge|release|deploy)(?:\s*(?:,|and|or|nor)\s*(?:merge|release|deploy))*\s*$)/i.test(s) || [...s.matchAll(controlledActionPattern)].some(match => !match.groups.git && technicalOperation(match.groups.verb, s.slice(match.index + match[0].length), saved)))
393
+ .join(" ").replace(/\b(?:please|and|then)\b/ig, "");
394
+ if (!/[\p{L}\p{N}]/u.test(implementation)) fail("describe the implementation as well as its delivery");
395
+ return { version, offsets: "utf16", source, spans };
396
+ }
397
+
398
+ export function validateOwnerDelivery(value) {
399
+ if (!value || ![1, 2, 3, 4, 5].includes(value.version) || typeof value.source !== "string" || stable(partitionOwnerDelivery(value.source, value.version, true)) !== stable(value)) fail("the saved ownership spans must exactly cover the original request and its supported publication clause");
400
+ return value;
401
+ }
402
+
403
+ export function assertDeliveryAnswer(task, text) {
404
+ const visible = outsideFeatureObjects(outsideLiterals(text));
405
+ // Answers may refine implementation, but cannot silently replace the approved
406
+ // publication contract, including a new prerequisite in a separate sentence.
407
+ // Legacy exact operation replays are handled before this check by the service.
408
+ if (publicationDirective(visible) || ownerPrerequisite(visible) || deliveryControl(visible)) fail("delivery changes in answers require a new clarified task request");
409
+ }
410
+
411
+ export function ownerImplementation(value) {
412
+ validateOwnerDelivery(value);
413
+ return value.spans.filter(s => s.owner === "implementation").map(s => value.source.slice(s.start, s.end));
414
+ }
415
+
416
+ export function ownerDeliveryFromPrd(prd) {
417
+ const section = prd?.sections?.[OWNER_DELIVERY_SECTION];
418
+ if (section === undefined) return null;
419
+ if ((String(prd.body || "").match(/^## Coordinator delivery contract\s*$/gm) || []).length !== 1) fail("the coordinator contract must have exactly one section");
420
+ const match = section.trim().match(/^```json\r?\n([\s\S]+)\r?\n```$/);
421
+ if (!match) fail("the coordinator contract must be one complete JSON block");
422
+ let value; try { value = JSON.parse(match[1]); } catch { fail("the coordinator contract is invalid JSON"); }
423
+ return validateOwnerDelivery(value);
424
+ }
425
+
426
+ export function ownerDeliveryEvidence(value, entry, status) {
427
+ if (!value) return [];
428
+ validateOwnerDelivery(value);
429
+ const delivery = entry?.completionEvidence?.delivery;
430
+ const fulfilled = ["fulfilled", "awaiting_integration"].includes(status) && matchingDeliveryEvidence(entry);
431
+ return value.spans.filter(s => s.owner === "coordinator").map((span, i) => ({ id: `delivery-${i + 1}`, kind: span.kind,
432
+ source: { start: span.start, end: span.end, text: value.source.slice(span.start, span.end) },
433
+ status: fulfilled ? "fulfilled" : "pending", evidence: fulfilled ? { source: "coordinator-observed", repository: delivery.repository, pr: delivery.pr, head: delivery.head, base: delivery.base, observedAt: delivery.observedAt } : null }));
434
+ }
435
+
436
+ export function matchingDeliveryEvidence(entry) {
437
+ const delivery = entry?.completionEvidence?.delivery;
438
+ return Boolean(delivery?.gate?.allow === true && !delivery.invalidatedAt &&
439
+ typeof entry.repo === "string" && entry.repo.trim().length > 0 &&
440
+ typeof entry.base === "string" && entry.base.trim().length > 0 &&
441
+ delivery.repository === entry.repo && delivery.pr === entry.pr && Number.isSafeInteger(delivery.pr) && delivery.pr > 0 &&
442
+ typeof delivery.head === "string" && delivery.head.length > 0 && delivery.head === entry.cursor?.fixed_sha && delivery.base === entry.base);
443
+ }