@open-mercato/cli 0.10.1-develop.8004.1.a8a79ee43f → 0.10.1-develop.8022.1.b6753de892

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 (102) hide show
  1. package/dist/agentic/guides/module-facts.json +162 -162
  2. package/dist/agentic/guides/module-facts.v2.json +162 -162
  3. package/dist/agentic/guides/modules/agent_orchestrator/index.md +1 -1
  4. package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
  5. package/dist/agentic/guides/modules/api_docs/index.md +1 -1
  6. package/dist/agentic/guides/modules/api_keys/index.md +1 -1
  7. package/dist/agentic/guides/modules/attachments/index.md +1 -1
  8. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  9. package/dist/agentic/guides/modules/auth/index.md +1 -1
  10. package/dist/agentic/guides/modules/availability/index.md +1 -1
  11. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  12. package/dist/agentic/guides/modules/carrier_inpost/index.md +1 -1
  13. package/dist/agentic/guides/modules/cart/index.md +1 -1
  14. package/dist/agentic/guides/modules/catalog/index.md +1 -1
  15. package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
  16. package/dist/agentic/guides/modules/channel_discord/index.md +1 -1
  17. package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
  18. package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
  19. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  20. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  21. package/dist/agentic/guides/modules/channel_resend/index.md +1 -1
  22. package/dist/agentic/guides/modules/channel_ses/index.md +1 -1
  23. package/dist/agentic/guides/modules/channel_smtp/index.md +1 -1
  24. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  25. package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
  26. package/dist/agentic/guides/modules/configs/index.md +1 -1
  27. package/dist/agentic/guides/modules/content/index.md +1 -1
  28. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  29. package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
  30. package/dist/agentic/guides/modules/customer_groups/index.md +1 -1
  31. package/dist/agentic/guides/modules/customers/index.md +1 -1
  32. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  33. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  34. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  35. package/dist/agentic/guides/modules/devices/index.md +1 -1
  36. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  37. package/dist/agentic/guides/modules/directory/index.md +1 -1
  38. package/dist/agentic/guides/modules/documents/index.md +1 -1
  39. package/dist/agentic/guides/modules/ecommerce/index.md +1 -1
  40. package/dist/agentic/guides/modules/entities/index.md +1 -1
  41. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  42. package/dist/agentic/guides/modules/events/index.md +1 -1
  43. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  44. package/dist/agentic/guides/modules/forms/index.md +1 -1
  45. package/dist/agentic/guides/modules/gateway_autopay/index.md +1 -1
  46. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  47. package/dist/agentic/guides/modules/gateway_tpay/index.md +1 -1
  48. package/dist/agentic/guides/modules/generators/index.md +1 -1
  49. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  50. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  51. package/dist/agentic/guides/modules/messages/index.md +1 -1
  52. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  53. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  54. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  55. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  56. package/dist/agentic/guides/modules/phone_calls/index.md +1 -1
  57. package/dist/agentic/guides/modules/planner/index.md +1 -1
  58. package/dist/agentic/guides/modules/portal/index.md +1 -1
  59. package/dist/agentic/guides/modules/progress/index.md +1 -1
  60. package/dist/agentic/guides/modules/promotions/index.md +1 -1
  61. package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
  62. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  63. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  64. package/dist/agentic/guides/modules/resources/index.md +1 -1
  65. package/dist/agentic/guides/modules/sales/index.md +1 -1
  66. package/dist/agentic/guides/modules/scheduler/index.md +1 -1
  67. package/dist/agentic/guides/modules/search/index.md +1 -1
  68. package/dist/agentic/guides/modules/security/index.md +1 -1
  69. package/dist/agentic/guides/modules/seeds/index.md +1 -1
  70. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  71. package/dist/agentic/guides/modules/sso/index.md +1 -1
  72. package/dist/agentic/guides/modules/staff/index.md +1 -1
  73. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  74. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  75. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  76. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  77. package/dist/agentic/guides/modules/telemetry/index.md +1 -1
  78. package/dist/agentic/guides/modules/tillio/index.md +1 -1
  79. package/dist/agentic/guides/modules/translations/index.md +1 -1
  80. package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
  81. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  82. package/dist/agentic/guides/modules/wms/index.md +1 -1
  83. package/dist/agentic/guides/modules/workflows/index.md +1 -1
  84. package/dist/agentic/guides/reference-module-facts.json +1 -1
  85. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +4 -0
  86. package/dist/agentic/guides/upstream/manifest.json +2 -2
  87. package/dist/agentic/shared/ai/harness/cases.json +2 -2
  88. package/dist/agentic/shared/ai/trackers/github.md +257 -2
  89. package/dist/agentic/shared/scripts/execution-sandbox.mjs +1 -0
  90. package/dist/lib/generators/openapi.js +7 -2
  91. package/dist/lib/generators/openapi.js.map +2 -2
  92. package/dist/lib/telemetry-init.js +1 -1
  93. package/dist/lib/telemetry-init.js.map +1 -1
  94. package/dist/lib/testing/integration.js +22 -0
  95. package/dist/lib/testing/integration.js.map +2 -2
  96. package/package.json +7 -7
  97. package/src/lib/__tests__/telemetry-init.test.ts +1 -1
  98. package/src/lib/generators/__tests__/openapi.test.ts +24 -0
  99. package/src/lib/generators/openapi.ts +7 -2
  100. package/src/lib/telemetry-init.ts +1 -1
  101. package/src/lib/testing/__tests__/integration.test.ts +24 -0
  102. package/src/lib/testing/integration.ts +23 -0
@@ -1,5 +1,5 @@
1
1
  # staff — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/staff
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # storage_s3 — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/storage-s3 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/storage-s3 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/storage-s3/src/modules/storage_s3
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # sync_akeneo — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/sync-akeneo 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/sync-akeneo 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/sync-akeneo/src/modules/sync_akeneo
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # sync_excel — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/sync_excel
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # system_status_overlays — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/enterprise 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/enterprise 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/enterprise/src/modules/system_status_overlays
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # telemetry — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/telemetry 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/telemetry 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/telemetry/src/modules/telemetry
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # tillio — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/tillio 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/tillio 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/tillio/src/modules/tillio
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # translations — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/translations
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # warranty_claims — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/warranty_claims
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # webhooks — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/webhooks 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/webhooks 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/webhooks/src/modules/webhooks
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # wms — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/wms
4
4
 
5
5
  ## Sections
@@ -1,5 +1,5 @@
1
1
  # workflows — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/core 0.10.1-develop.8004.1.a8a79ee43f; core 0.10.1-develop.8004.1.a8a79ee43f — R1 staleness stamp -->
2
+ <!-- generated from @open-mercato/core 0.10.1-develop.8022.1.b6753de892; core 0.10.1-develop.8022.1.b6753de892 — R1 staleness stamp -->
3
3
  Source root: node_modules/@open-mercato/core/src/modules/workflows
4
4
 
5
5
  ## Sections
@@ -1185,7 +1185,7 @@
1185
1185
  "facts": {
1186
1186
  "title": "Example Module",
1187
1187
  "description": "Demonstrates pages, APIs, entities and DI.",
1188
- "coreVersion": "0.10.1-develop.8004.1.a8a79ee43f",
1188
+ "coreVersion": "0.10.1-develop.8022.1.b6753de892",
1189
1189
  "sourcePackage": null,
1190
1190
  "sourceVersion": null,
1191
1191
  "sourceRoot": "src/modules/example",
@@ -635,6 +635,10 @@ STABLE tools/execute writes bypass approval; no bridge. [Migrate](UPGRADE_NOTES.
635
635
 
636
636
  SCIM adds 429/503 before credentials; honor Retry-After. Optional `beforeAuth: ApiRouteBeforeAuth` is additive. See [contract and migration](.ai/specs/enterprise/implemented/SPEC-ENT-004-2026-02-19-sso-directory-sync.md#migration--backward-compatibility).
637
637
 
638
+ ## Encryption maintenance safety gates (2026-11-02, #6767)
639
+
640
+ CLI safety confirmations changed; stop writers and restart replicas. [Flags, migration and stable formats](UPGRADE_NOTES.md#encryption-maintenance-safety-confirmations-6767).
641
+
638
642
  ## Staff Logout Uses Post/Redirect/Get (2026-11-05)
639
643
 
640
644
  `POST /api/auth/logout` now returns `303 See Other` instead of `307`, including the cookieless branch ([#7383](https://github.com/open-mercato/open-mercato/issues/7383)). Browsers follow `Location: /login` with GET rather than repeating the logout POST. This intentionally corrects the status documented in the 2026-10-13 entry above; shared redirect helper defaults remain 307.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": 1,
3
- "generator": "@open-mercato/cli@0.10.1-develop.8004.1.a8a79ee43f",
3
+ "generator": "@open-mercato/cli@0.10.1-develop.8022.1.b6753de892",
4
4
  "files": {
5
5
  "AGENTS.md": "35c948a32e06ca64b4706970c55ec60946412b410938b22478233a2c5c43df56",
6
- "BACKWARD_COMPATIBILITY.md": "1b75c2ca2f16f3f6fe8bab25e059e49369bb75270b6cb7c8f4070f1e2a95169b"
6
+ "BACKWARD_COMPATIBILITY.md": "4e85dfc08b0e9542c78415e25a020e06e4ec3c3bb664b278b5aa3faa3523d873"
7
7
  }
8
8
  }
@@ -520,7 +520,7 @@
520
520
  ],
521
521
  "maxContextFiles": 8,
522
522
  "maxInitialContextBytes": 65536,
523
- "maxTotalContextBytes": 122880,
523
+ "maxTotalContextBytes": 126976,
524
524
  "relatedCases": [
525
525
  "OMH-008"
526
526
  ]
@@ -5145,7 +5145,7 @@
5145
5145
  ],
5146
5146
  "maxContextFiles": 8,
5147
5147
  "maxInitialContextBytes": 65536,
5148
- "maxTotalContextBytes": 122880,
5148
+ "maxTotalContextBytes": 126976,
5149
5149
  "relatedCases": [
5150
5150
  "OMH-065"
5151
5151
  ]
@@ -14,7 +14,7 @@ At runtime: `om-setup-agent-pipeline` copies this file into the repository at `.
14
14
  - Issue and PR identifiers are numbers; in text they are written `#123`.
15
15
  - A PR is linked to the issue it resolves with `Fixes #{issueId}` (or `Closes #{issueId}`) in the PR body; GitHub then closes the issue on merge. To reference without auto-closing, use a plain issue link.
16
16
  - PRs open as **drafts** when a skill says so; a human (or **mark-pr-ready**) promotes them.
17
- - Claim/lock signals on an issue or PR are: assignee set to the automation user, the `in-progress` label, and a `🤖`-prefixed claim comment. All three are set on claim; the label is guarded (below). The `ci-monitoring` label is **not** a claim/lock signal: it marks a PR whose work is finished and already reported while an agent watches CI, so a PR carrying `ci-monitoring` without any of the three signals above MUST be treated as unclaimed and is free to claim.
17
+ - GitHub claim ownership uses the create-only `refs/om-locks/{kind}-{number}` ref through **get-lock**, **acquire-lock**, and exact-SHA **release-lock**. Acquire first, retain run/comment ids and lock SHA, then assign when supported and verify guarded `in-progress`. For confirmed unassignable bot accounts, retain the atomic lock, timestamped claim comment and verified label without self-assignment. Foreign assignees and live or uncertain claims remain blockers. The `ci-monitoring` label is **not** a claim/lock signal: it marks a PR whose work is finished and already reported while an agent watches CI, so a PR carrying `ci-monitoring` without any of the three signals above MUST be treated as unclaimed and is free to claim.
18
18
  - Long, multi-line comment bodies are posted with `--body-file` (or a heredoc via process substitution) so formatting is preserved.
19
19
  - CI status truth comes from **get-pr-checks**; the set of *required* checks comes from **get-required-checks** (branch protection). When branch protection is not readable (404), treat every reported check as required.
20
20
 
@@ -234,7 +234,7 @@ gh api repos/{owner}/{repo}/issues/comments/{commentId} --jq '{body,user:.user.l
234
234
  #### list-issue-comments
235
235
  `{issueId or prNumber}` → conversation comments (PR conversation comments are issue comments on GitHub).
236
236
  ```bash
237
- gh api repos/{owner}/{repo}/issues/{number}/comments --jq '.[] | {id,user:.user.login,body}'
237
+ gh api --paginate repos/{owner}/{repo}/issues/{number}/comments --jq '.[] | {id,user:.user.login,body,createdAt:.created_at,updatedAt:.updated_at}'
238
238
  ```
239
239
 
240
240
  #### update-comment
@@ -243,6 +243,219 @@ gh api repos/{owner}/{repo}/issues/{number}/comments --jq '.[] | {id,user:.user.
243
243
  gh api -X PATCH repos/{owner}/{repo}/issues/comments/{commentId} -F body=@<path>
244
244
  ```
245
245
 
246
+ ```bash
247
+ tracker_repo() {
248
+ if [ -n "${REPO:-}" ]; then printf '%s\n' "$REPO";
249
+ else gh repo view --json nameWithOwner --jq .nameWithOwner; fi
250
+ }
251
+ ```
252
+
253
+ #### get-lock
254
+
255
+ `{kind}` (`issue` or `pr`), `{item}` → `null` for an absent exact lock, or
256
+ `{ref,sha,repo,kind,item,runId,actor,createdAt}`. The authoritative GitHub lock is
257
+ `refs/om-locks/{kind}-{item}`. Its SHA points to an otherwise empty metadata
258
+ commit using the configured base's existing tree, with no branch/history edits.
259
+ The commit message binds the unique run id and actor; a base SHA alone cannot
260
+ identify two runs sharing a bot login. `createdAt` comes from the server commit
261
+ metadata, not a comment timestamp. Unknown/malformed refs and failed reads block.
262
+ Load these helpers before **acquire-lock** or **release-lock**:
263
+
264
+ ```bash
265
+ gh_lock_target() {
266
+ case "$1" in issue|pr) ;; *) return 1 ;; esac
267
+ case "$2" in ''|*[!0-9]*) return 1 ;; esac
268
+ LOCK_REPO=$(tracker_repo) || return 1
269
+ LOCK_REF="refs/om-locks/$1-$2"
270
+ }
271
+ gh_get_lock() {
272
+ gh_lock_target "$1" "$2" || return 1
273
+ lock_refs=$(gh api "repos/$LOCK_REPO/git/matching-refs/om-locks/$1-$2") || return 1
274
+ lock_entry=$(printf '%s' "$lock_refs" | jq -c --arg ref "$LOCK_REF" '
275
+ if type != "array" then error("Invalid ref list") else
276
+ [.[] | select(.ref == $ref)] | if length == 0 then null
277
+ elif length == 1 and .[0].object.type == "commit" then .[0]
278
+ else error("Ambiguous lock ref") end end') || return 1
279
+ if [ "$lock_entry" = "null" ]; then printf 'null\n'; return 0; fi
280
+ lock_sha=$(printf '%s' "$lock_entry" | jq -er '.object.sha | select(test("^[0-9a-f]{40}$"))') || return 1
281
+ lock_commit=$(gh api "repos/$LOCK_REPO/git/commits/$lock_sha") || return 1
282
+ printf '%s' "$lock_commit" | jq -ce --arg repo "$LOCK_REPO" --arg kind "$1" --arg item "$2" --arg ref "$LOCK_REF" --arg sha "$lock_sha" '
283
+ .committer.date as $created |
284
+ .message | select(startswith("om-claim-v1\n")) | ltrimstr("om-claim-v1\n") | fromjson |
285
+ select(.repo == $repo and .kind == $kind and .item == $item and
286
+ (.runId | type == "string" and length > 0) and
287
+ (.actor | type == "string" and length > 0) and ($created | type == "string")) |
288
+ . + {ref:$ref,sha:$sha,createdAt:$created}'
289
+ }
290
+ gh_get_lock "{kind}" "{number}"
291
+ ```
292
+
293
+ #### acquire-lock
294
+
295
+ `{kind}`, `{item}`, unique run id, authenticated actor, resolved base SHA →
296
+ `{status: acquired|owned|conflict, lock: <get-lock result>}`. On GitHub this is
297
+ mandatory, including for unassignable bots and PRs in split-provider setups.
298
+ Requires contents-write and authenticated Git transport for eventual release.
299
+ Never switch to comments on 403/422/transport errors: an existing ref is a
300
+ conflict only after an exact successful read proves its owner. Reconcile an
301
+ uncertain POST through **get-lock** before retrying. No labels/assignment/comment
302
+ candidate or work is produced by a losing fresh run.
303
+
304
+ ```bash
305
+ gh_acquire_lock() {
306
+ # kind, item, run id, authenticated actor, configured base SHA
307
+ gh_lock_target "$1" "$2" || return 1
308
+ case "$3" in ''|*[!A-Za-z0-9._-]*) return 1 ;; esac
309
+ [ -n "$4" ] || return 1
310
+ printf '%s' "$5" | grep -Eq '^[0-9a-f]{40}$' || return 1
311
+ lock_before=$(gh_get_lock "$1" "$2") || return 1
312
+ if [ "$lock_before" != "null" ]; then
313
+ printf '%s' "$lock_before" | jq --arg run "$3" --arg actor "$4" '
314
+ {status:(if .runId == $run and .actor == $actor then "owned" else "conflict" end),lock:.}'
315
+ return 0
316
+ fi
317
+ lock_tree=$(gh api "repos/$LOCK_REPO/git/commits/$5" --jq '.tree.sha') || return 1
318
+ lock_message=$(jq -cn --arg repo "$LOCK_REPO" --arg kind "$1" --arg item "$2" --arg run "$3" --arg actor "$4" '
319
+ "om-claim-v1\n" + ({repo:$repo,kind:$kind,item:$item,runId:$run,actor:$actor}|tojson)') || return 1
320
+ lock_created=$(jq -n --argjson message "$lock_message" --arg tree "$lock_tree" --arg parent "$5" '
321
+ {message:$message,tree:$tree,parents:[$parent]}' |
322
+ gh api -X POST "repos/$LOCK_REPO/git/commits" --input -) || return 1
323
+ lock_proposed=$(printf '%s' "$lock_created" | jq -er '.sha | select(test("^[0-9a-f]{40}$"))') || return 1
324
+ # Create-only POST is the ownership decision; never PATCH/update an existing ref.
325
+ if gh api -X POST "repos/$LOCK_REPO/git/refs" -f "ref=$LOCK_REF" -f "sha=$lock_proposed" >/dev/null 2>&1; then
326
+ lock_posted=true
327
+ else
328
+ lock_posted=false
329
+ fi
330
+ lock_after=$(gh_get_lock "$1" "$2") || return 1
331
+ [ "$lock_after" != "null" ] || return 1
332
+ printf '%s' "$lock_after" | jq --arg run "$3" --arg actor "$4" --arg sha "$lock_proposed" --arg posted "$lock_posted" '
333
+ {status:(if .sha == $sha and .runId == $run and .actor == $actor then "acquired"
334
+ elif .runId == $run and .actor == $actor then "owned"
335
+ elif $posted == "false" then "conflict" else error("Lock replaced after creation") end),lock:.}'
336
+ }
337
+ gh_acquire_lock "{kind}" "{number}" "$CLAIM_RUN_ID" "$CURRENT_USER" "$BASE_SHA"
338
+ ```
339
+
340
+ Persist the returned lock SHA plus run id before comment/label writes; reconcile
341
+ the same run on resume. A metadata commit created by a losing contender may be
342
+ unreferenced and garbage-collected; it contains no secrets or new project content.
343
+
344
+ #### release-lock
345
+
346
+ `{kind}`, `{item}`, exact expected lock SHA, expected owner run id/actor → delete
347
+ only that exact ref. Normal callers pass their own verified identity; recovery
348
+ passes the observed previous owner only after the shared stale/force/UI recovery
349
+ authorization checks. Keep the lock while cleaning up owned labels and marking
350
+ the comment released; delete the ref last. A loser never calls this operation.
351
+
352
+ ```bash
353
+ gh_release_lock() {
354
+ # kind, item, expected SHA, expected owner's run id and actor
355
+ gh_lock_target "$1" "$2" || return 1
356
+ printf '%s' "$3" | grep -Eq '^[0-9a-f]{40}$' || return 1
357
+ lock_current=$(gh_get_lock "$1" "$2") || return 1
358
+ [ "$lock_current" != "null" ] || { printf 'absent\n'; return 0; }
359
+ printf '%s' "$lock_current" | jq -e --arg sha "$3" --arg run "$4" --arg actor "$5" '
360
+ .sha == $sha and .runId == $run and .actor == $actor' >/dev/null || return 1
361
+ lock_url=$(gh repo view "$LOCK_REPO" --json url --jq .url) || return 1
362
+ # Git's explicit expected-SHA lease is atomic compare-and-delete. REST DELETE
363
+ # has no expected-SHA condition and can remove a successor during recovery.
364
+ git push --porcelain --force-with-lease="$LOCK_REF:$3" "$lock_url.git" ":$LOCK_REF" || return 1
365
+ lock_remaining=$(gh_get_lock "$1" "$2") || return 1
366
+ if [ "$lock_remaining" = "null" ]; then printf 'released\n'
367
+ elif printf '%s' "$lock_remaining" | jq -e --arg sha "$3" '.sha != $sha' >/dev/null; then
368
+ printf 'released; successor retained\n'
369
+ else return 1; fi
370
+ }
371
+ gh_release_lock "{kind}" "{number}" "$CLAIM_LOCK_SHA" "$CLAIM_RUN_ID" "$CURRENT_USER"
372
+ ```
373
+
374
+ This narrowly scoped lease-deletion is authorized lock housekeeping, never a
375
+ force-push of project branches. Resolve the target repository's GitHub URL as
376
+ above rather than assuming `origin` is the target. Do not change credentials,
377
+ remove hooks or retry with an unconditional REST DELETE when Git transport fails.
378
+ Absent is idempotent; a changed SHA/owner blocks and preserves the successor.
379
+
380
+ #### get-claim-reset
381
+
382
+ `{kind}`, `{item}` → latest tracker-audited human `claim-reset` label application
383
+ as `{id,createdAt,actor}`, or `null` if none. Read every event page, including past
384
+ applications after label removal. Verify the event actor is a human permitted to
385
+ manage item labels; bot applications and comment/body text never authorize reset.
386
+ A failed/unsupported audit read is not `null`: stop recovery and report the gap.
387
+ This cutoff permanently retires claim comments created strictly before it;
388
+ same-second/unknown timestamps are uncertain and remain blocking. It does not
389
+ relax QA, merge, scheduler reservations or command/process isolation.
390
+
391
+ ```bash
392
+ gh_get_claim_reset() {
393
+ case "$1" in ''|*[!0-9]*) return 1 ;; esac
394
+ reset_pages=$(gh api --paginate --slurp "repos/$(tracker_repo)/issues/$1/events?per_page=100") || return 1
395
+ printf '%s' "$reset_pages" | jq -e 'type == "array" and all(.[]; type == "array")' >/dev/null || return 1
396
+ reset_event=$(printf '%s' "$reset_pages" | jq '
397
+ [ .[][] | select(.event == "labeled" and .label.name == "claim-reset" and .actor.type == "User") ]
398
+ | sort_by(.created_at, .id) | .[-1] // null') || return 1
399
+ if [ "$reset_event" = "null" ]; then printf 'null\n'; return 0; fi
400
+ reset_actor=$(printf '%s' "$reset_event" | jq -r '.actor.login') || return 1
401
+ case "$reset_actor" in ''|*[!A-Za-z0-9-]*) return 1 ;; esac
402
+ reset_permission=$(gh api "repos/$(tracker_repo)/collaborators/$reset_actor/permission") || return 1
403
+ printf '%s' "$reset_permission" | jq -e '
404
+ (.permission == "admin" or .permission == "write" or .permission == "maintain" or .permission == "triage")
405
+ or (.user.permissions | .admin == true or .maintain == true or .push == true or .triage == true)' >/dev/null || return 1
406
+ printf '%s' "$reset_event" | jq '{id: (.id | tostring), createdAt: .created_at, actor: .actor.login}'
407
+ }
408
+ gh_get_claim_reset "{number}"
409
+ ```
410
+
411
+ The platform event plus repository-permission read proves human label authority.
412
+ A label still present is an operator-visible recovery breadcrumb, not a permanent
413
+ bypass. Removing it does not revive retired claims; remove and reapply to request
414
+ a later reset. Never apply this label automatically.
415
+
416
+ #### claim-winner
417
+
418
+ `{kind}`, `{item}`, `LIVE_CLAIM_IDS` (JSON array of comment handles from a complete
419
+ fresh **list-issue-comments** read, selected by the skill's live/stale policy) →
420
+ `{id,user,body}` for the lowest server creation-order candidate, or `null` when
421
+ none is visible. Missing/unreadable/incomplete data is an error, never an empty
422
+ success. Require the caller's own id in the fresh candidate set before proceeding.
423
+ Ignore explicitly `released`/`withdrawn` comments even if passed as live. Do not
424
+ filter by actor: two contenders may share one bot account. Numeric ids compare
425
+ numerically; opaque ids use creation time then stable id. The caller repeats the
426
+ read/election and performs the post-label ownership check; this is not atomic.
427
+
428
+ ```bash
429
+ gh_claim_winner() {
430
+ # $1 = item number; $2 = LIVE_CLAIM_IDS as JSON string handles.
431
+ case "$1" in ''|*[!0-9]*) return 1 ;; esac
432
+ printf '%s' "$2" | jq -e 'type == "array" and all(.[]; type == "string")' >/dev/null || return 1
433
+ claim_pages=$(gh api --paginate --slurp "repos/$(tracker_repo)/issues/$1/comments?per_page=100") || return 1
434
+ printf '%s' "$claim_pages" | jq -e 'type == "array" and all(.[]; type == "array")' >/dev/null || return 1
435
+ printf '%s' "$claim_pages" | jq --argjson live "$2" '
436
+ [ .[][] | select((.id | tostring) as $id | $live | index($id))
437
+ | select((.body | test("(?m)^Claim-State: (released|withdrawn)\\r?$")) | not)
438
+ | {id: (.id | tostring), user: .user.login, body} ]
439
+ | sort_by(.id | [length, .]) | .[0] // null'
440
+ }
441
+ gh_claim_winner "{number}" "$LIVE_CLAIM_IDS"
442
+ ```
443
+
444
+ All comment pages are read before ranking. Preserve REST numeric ids as string
445
+ handles at the operation boundary (do not substitute GraphQL node ids).
446
+
447
+ #### withdraw-claim
448
+
449
+ `{kind}`, `{item}`, `{commentId}`, full terminal body file, expected authenticated
450
+ actor and `Claim-Run` → rewrite only that run's original candidate through **update-comment**,
451
+ then read it back through **list-issue-comments**. Retain its legacy first line,
452
+ marker and run id; change `Claim-State: active` to `withdrawn` (loser) or `released`
453
+ (owner after cleanup), with the reason/winner. Before writing, verify exact item,
454
+ comment id, authenticated author and run id; a mismatch stops cleanup. Do not
455
+ edit a foreign candidate or delete its label. Return the same id and verified
456
+ terminal state; failed update/read-back is a blocker. Never fall back to a new
457
+ comment leaving the old active candidate in place.
458
+
246
459
  ### Pull requests
247
460
 
248
461
  #### get-pr
@@ -478,3 +691,45 @@ gh label create risk-low --color 0e8a16 --description "Isolated, low bl
478
691
  gh label create risk-medium --color fbca04 --description "Ordinary change with tests"
479
692
  gh label create risk-high --color b60205 --description "Wide blast radius, review deeply"
480
693
  ```
694
+
695
+ #### list-review-comments
696
+ `{prNumber}` → every inline review comment on the diff (the conversation comments come from **list-issue-comments**; these are the ones anchored to a file and line).
697
+ ```bash
698
+ gh api --paginate repos/{owner}/{repo}/pulls/{prNumber}/comments \
699
+ --jq '.[] | {id,user:.user.login,path,line:(.line // .original_line),body,url:.html_url,reply_to:.in_reply_to_id}'
700
+ ```
701
+ REST does not expose a thread's resolved state (that lives in GraphQL's review threads), so treat every returned comment as potentially open and judge it against the current diff. `reply_to` is non-null on replies, which is what lets you reconstruct a thread. Consumers treat an unavailable operation as "inline feedback out of reach", not as a failure: they fall back to review bodies plus conversation comments and state the gap in their report.
702
+
703
+
704
+ #### approve-pending-runs
705
+ Head SHA → approve every workflow run on that commit that is waiting for maintainer approval (`conclusion` `action_required` — GitHub holds runs from first-time or outside contributors until a maintainer approves them). Prints the approved run ids; an empty result means nothing was waiting. Approving lets the contributor's workflow code run with this repository's CI, so callers approve only on a PR a human has authorized for automation (e.g. an `automerge` label) and read the diff's workflow changes first.
706
+ ```bash
707
+ gh api "repos/{owner}/{repo}/actions/runs?head_sha={headSha}&status=action_required&per_page=100" \
708
+ --jq '.workflow_runs[].id' | while read -r run; do
709
+ gh api -X POST "repos/{owner}/{repo}/actions/runs/$run/approve" >/dev/null && echo "$run"
710
+ done
711
+ ```
712
+
713
+
714
+ #### get-pr-author-association
715
+ `{prNumber}` → `{login, association}`, normalized from GitHub's `author_association`. GitHub reports `COLLABORATOR` for anyone with explicit repository access who is not an organization member, so on an organization-owned repository that is an `outside-collaborator`. `FIRST_TIMER` (first PR anywhere on GitHub) folds into `first-time-contributor`, and `MANNEQUIN` folds into `none`.
716
+ ```bash
717
+ gh api "repos/{owner}/{repo}/pulls/{prNumber}" --jq '{login: .user.login, association: (
718
+ .author_association as $a | .base.repo.owner.type as $t |
719
+ if $a == "OWNER" then "owner"
720
+ elif $a == "MEMBER" then "member"
721
+ elif $a == "COLLABORATOR" then (if $t == "Organization" then "outside-collaborator" else "collaborator" end)
722
+ elif $a == "CONTRIBUTOR" then "contributor"
723
+ elif $a == "FIRST_TIME_CONTRIBUTOR" or $a == "FIRST_TIMER" then "first-time-contributor"
724
+ else "none" end)}'
725
+ ```
726
+
727
+
728
+ #### close-pr
729
+ `{prNumber}` → close without merging; preserve the head branch, linked issues,
730
+ title and body. Post/update the caller's evidence note before calling. A fresh
731
+ **get-pr** must confirm `CLOSED` and no merge before reporting success; an
732
+ already merged PR is never reported as closed-obsolete.
733
+ ```bash
734
+ gh api -X PATCH repos/{owner}/{repo}/pulls/{prNumber} -f state=closed
735
+ ```
@@ -307,6 +307,7 @@ function linuxInvocation({ command, args, cwd, writableRoots, readOnlyRoots, net
307
307
  sandboxArgs.push('--ro-bind', root, root)
308
308
  }
309
309
  for (const file of networkReadable) sandboxArgs.push('--ro-bind', file, file)
310
+ sandboxArgs.push('--remount-ro', '/', '--remount-ro', '/tmp')
310
311
  const realCwd = regularDirectory(cwd, 'sandbox cwd')
311
312
  sandboxArgs.push('--chdir', realCwd, '--', resolveExecutable(command, env), ...args)
312
313
  return { command: bubblewrap, args: sandboxArgs, cwd: realCwd, env }
@@ -1,4 +1,5 @@
1
1
  import * as fs from "node:fs";
2
+ import { randomBytes } from "node:crypto";
2
3
  import * as path from "node:path";
3
4
  import { getEnabledModuleIdentities } from "../module-identity.js";
4
5
  import { isModuleRouteFile } from "./scanner.js";
@@ -208,7 +209,7 @@ async function generateOpenApiViaBundle(routes, resolver, quiet) {
208
209
  }
209
210
  const cacheDir = path.join(rootDir, "node_modules", ".cache");
210
211
  if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
211
- const bundlePath = path.join(cacheDir, "_openapi-bundle.mjs");
212
+ const bundlePath = path.join(cacheDir, `_openapi-bundle-${process.pid}-${randomBytes(6).toString("hex")}.mjs`);
212
213
  let inputPaths = [];
213
214
  const importLines = [
214
215
  `import { buildOpenApiDocument } from ${JSON.stringify(generatorPath)};`
@@ -471,7 +472,11 @@ process.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>
471
472
  await esbuild.stop();
472
473
  } catch {
473
474
  }
474
- for (const file of ["_openapi-register.mjs", "_openapi-loader.mjs", "_next-stub.cjs"]) {
475
+ try {
476
+ fs.unlinkSync(bundlePath);
477
+ } catch {
478
+ }
479
+ for (const file of ["_openapi-bundle.mjs", "_openapi-register.mjs", "_openapi-loader.mjs", "_next-stub.cjs"]) {
475
480
  try {
476
481
  fs.unlinkSync(path.join(cacheDir, file));
477
482
  } catch {