@seasonkoh/webaz 0.1.23 → 0.1.25

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 (187) hide show
  1. package/README.md +2 -0
  2. package/dist/layer0-foundation/L0-1-database/db-backends/pg-backend.js +51 -0
  3. package/dist/layer0-foundation/L0-1-database/db-backends/sql-dialect-datetime.js +437 -0
  4. package/dist/layer0-foundation/L0-1-database/db-backends/sql-placeholders.js +98 -0
  5. package/dist/layer0-foundation/L0-1-database/db.js +65 -0
  6. package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +13 -11
  7. package/dist/layer0-foundation/L0-2-state-machine/transitions.js +1 -1
  8. package/dist/layer0-foundation/L0-5-manifest/manifest.js +13 -11
  9. package/dist/layer1-agent/L1-1-mcp-server/server.js +198 -83
  10. package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +14 -12
  11. package/dist/layer2-business/L2-6-notifications/notification-engine.js +8 -5
  12. package/dist/layer2-business/L2-7-snf/snf-engine.js +16 -14
  13. package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +18 -10
  14. package/dist/layer2-business/L2-9-contribution/build-reputation-engine.js +37 -23
  15. package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +173 -0
  16. package/dist/layer2-business/L2-9-contribution/build-task-participation.js +47 -0
  17. package/dist/layer2-business/L2-9-contribution/build-task-read.js +222 -0
  18. package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +10 -2
  19. package/dist/layer2-business/L2-9-contribution/canonical-contribution-target.js +16 -0
  20. package/dist/layer2-business/L2-9-contribution/contribution-display-envelope.js +40 -0
  21. package/dist/layer2-business/L2-9-contribution/contribution-score-contract.js +36 -0
  22. package/dist/layer2-business/L2-9-contribution/contribution-score-evidence.js +61 -0
  23. package/dist/layer2-business/L2-9-contribution/github-credential/canonical.js +60 -0
  24. package/dist/layer2-business/L2-9-contribution/github-credential/github-credential.schema.js +140 -0
  25. package/dist/layer2-business/L2-9-contribution/github-credential/github-fetch-adapter.js +437 -0
  26. package/dist/layer2-business/L2-9-contribution/github-credential/self-consistency.js +38 -0
  27. package/dist/layer2-business/L2-9-contribution/github-credential/verifier.js +231 -0
  28. package/dist/layer2-business/L2-9-contribution/github-credential-ingestion-engine.js +145 -0
  29. package/dist/layer2-business/L2-9-contribution/github-credential-store.js +115 -0
  30. package/dist/layer2-business/L2-9-contribution/identity-binding-engine.js +134 -0
  31. package/dist/layer2-business/L2-9-contribution/identity-binding-store.js +101 -0
  32. package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-engine.js +126 -0
  33. package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-store.js +30 -0
  34. package/dist/layer2-business/L2-9-contribution/identity-claim-engine.js +109 -0
  35. package/dist/layer2-business/L2-9-contribution/identity-claim-fact-precondition.js +22 -0
  36. package/dist/layer2-business/L2-9-contribution/identity-claim-proof-verifier.js +97 -0
  37. package/dist/layer2-business/L2-9-contribution/identity-claim-read.js +59 -0
  38. package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +129 -0
  39. package/dist/layer2-business/L2-notes/note-photo-storage.js +4 -2
  40. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +17 -15
  41. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +11 -8
  42. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +9 -8
  43. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +11 -8
  44. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +22 -16
  45. package/dist/pwa/acp-feed.js +13 -1
  46. package/dist/pwa/contract-fingerprint.js +2 -0
  47. package/dist/pwa/endpoint-actions.js +5 -1
  48. package/dist/pwa/goal-index.js +8 -8
  49. package/dist/pwa/human-presence.js +62 -0
  50. package/dist/pwa/public/app.js +575 -68
  51. package/dist/pwa/public/i18n.js +29 -20
  52. package/dist/pwa/public/index.html +1 -0
  53. package/dist/pwa/public/openapi.json +2 -2
  54. package/dist/pwa/rate-limit.js +22 -0
  55. package/dist/pwa/routes/account-deletion.js +15 -13
  56. package/dist/pwa/routes/addresses.js +10 -9
  57. package/dist/pwa/routes/admin-admins.js +13 -14
  58. package/dist/pwa/routes/admin-analytics.js +109 -69
  59. package/dist/pwa/routes/admin-catalog.js +13 -11
  60. package/dist/pwa/routes/admin-editor-picks.js +15 -10
  61. package/dist/pwa/routes/admin-events.js +5 -3
  62. package/dist/pwa/routes/admin-health.js +2 -1
  63. package/dist/pwa/routes/admin-moderation.js +26 -29
  64. package/dist/pwa/routes/admin-ops.js +22 -21
  65. package/dist/pwa/routes/admin-protocol-params.js +16 -19
  66. package/dist/pwa/routes/admin-reports.js +23 -21
  67. package/dist/pwa/routes/admin-tokenomics.js +26 -25
  68. package/dist/pwa/routes/admin-users-lifecycle.js +37 -40
  69. package/dist/pwa/routes/admin-users-query.js +54 -53
  70. package/dist/pwa/routes/admin-verifier-flow.js +82 -41
  71. package/dist/pwa/routes/admin-verifier-whitelist.js +55 -27
  72. package/dist/pwa/routes/admin-wallet-ops.js +7 -5
  73. package/dist/pwa/routes/agent-buy.js +46 -22
  74. package/dist/pwa/routes/agent-governance.js +52 -56
  75. package/dist/pwa/routes/ai.js +7 -5
  76. package/dist/pwa/routes/analytics.js +43 -41
  77. package/dist/pwa/routes/anchors.js +19 -20
  78. package/dist/pwa/routes/announcements.js +13 -13
  79. package/dist/pwa/routes/arbitrator.js +97 -31
  80. package/dist/pwa/routes/auction.js +153 -114
  81. package/dist/pwa/routes/auth-login.js +6 -4
  82. package/dist/pwa/routes/auth-read.js +11 -9
  83. package/dist/pwa/routes/auth-register.js +35 -20
  84. package/dist/pwa/routes/auth-sessions.js +12 -11
  85. package/dist/pwa/routes/blocklist.js +16 -15
  86. package/dist/pwa/routes/build-feedback.js +10 -9
  87. package/dist/pwa/routes/build-reputation.js +6 -2
  88. package/dist/pwa/routes/build-tasks.js +45 -13
  89. package/dist/pwa/routes/buyer-feeds.js +27 -25
  90. package/dist/pwa/routes/cart.js +16 -15
  91. package/dist/pwa/routes/charity.js +212 -150
  92. package/dist/pwa/routes/chat.js +42 -43
  93. package/dist/pwa/routes/checkin-tasks.js +10 -9
  94. package/dist/pwa/routes/checkout-helpers.js +12 -10
  95. package/dist/pwa/routes/claim-initiators.js +34 -14
  96. package/dist/pwa/routes/claim-verify.js +86 -53
  97. package/dist/pwa/routes/claim-voting.js +43 -18
  98. package/dist/pwa/routes/contribution-identity.js +147 -0
  99. package/dist/pwa/routes/contribution-score.js +19 -0
  100. package/dist/pwa/routes/coupons.js +19 -16
  101. package/dist/pwa/routes/dashboards.js +18 -16
  102. package/dist/pwa/routes/dispute-cases.js +25 -24
  103. package/dist/pwa/routes/disputes-read.js +45 -51
  104. package/dist/pwa/routes/disputes-write.js +124 -61
  105. package/dist/pwa/routes/evidence.js +9 -9
  106. package/dist/pwa/routes/external-anchors.js +13 -12
  107. package/dist/pwa/routes/feedback.js +29 -33
  108. package/dist/pwa/routes/flash-sales.js +18 -16
  109. package/dist/pwa/routes/follows.js +25 -24
  110. package/dist/pwa/routes/governance-auto-deactivate.js +21 -9
  111. package/dist/pwa/routes/governance-onboarding.js +70 -59
  112. package/dist/pwa/routes/group-buys.js +22 -22
  113. package/dist/pwa/routes/growth.js +33 -30
  114. package/dist/pwa/routes/import-product.js +12 -10
  115. package/dist/pwa/routes/kyc.js +9 -8
  116. package/dist/pwa/routes/leaderboard.js +20 -18
  117. package/dist/pwa/routes/listings.js +23 -22
  118. package/dist/pwa/routes/logistics.js +10 -8
  119. package/dist/pwa/routes/manifests.js +27 -27
  120. package/dist/pwa/routes/me-data.js +23 -21
  121. package/dist/pwa/routes/notifications.js +7 -6
  122. package/dist/pwa/routes/offers.js +30 -12
  123. package/dist/pwa/routes/orders-action.js +33 -17
  124. package/dist/pwa/routes/orders-create.js +75 -20
  125. package/dist/pwa/routes/orders-read.js +21 -20
  126. package/dist/pwa/routes/p2p-products.js +30 -18
  127. package/dist/pwa/routes/payments-governance.js +61 -56
  128. package/dist/pwa/routes/peers.js +9 -8
  129. package/dist/pwa/routes/pin-receipts.js +13 -13
  130. package/dist/pwa/routes/products-aliases.js +12 -10
  131. package/dist/pwa/routes/products-claims.js +36 -17
  132. package/dist/pwa/routes/products-create.js +53 -38
  133. package/dist/pwa/routes/products-crud.js +17 -16
  134. package/dist/pwa/routes/products-links.js +49 -26
  135. package/dist/pwa/routes/products-list.js +6 -4
  136. package/dist/pwa/routes/products-meta.js +40 -39
  137. package/dist/pwa/routes/products-update.js +19 -5
  138. package/dist/pwa/routes/profile-credentials.js +14 -16
  139. package/dist/pwa/routes/profile-identity.js +14 -13
  140. package/dist/pwa/routes/profile-location.js +7 -6
  141. package/dist/pwa/routes/profile-placement.js +19 -17
  142. package/dist/pwa/routes/profile-prefs.js +11 -11
  143. package/dist/pwa/routes/promoter.js +55 -49
  144. package/dist/pwa/routes/public-build-tasks.js +19 -0
  145. package/dist/pwa/routes/public-utils.js +108 -46
  146. package/dist/pwa/routes/push.js +16 -15
  147. package/dist/pwa/routes/ratings.js +30 -30
  148. package/dist/pwa/routes/recover-key.js +13 -12
  149. package/dist/pwa/routes/referral.js +37 -32
  150. package/dist/pwa/routes/reputation.js +3 -2
  151. package/dist/pwa/routes/returns.js +76 -73
  152. package/dist/pwa/routes/reviews.js +41 -18
  153. package/dist/pwa/routes/rewards-apply.js +16 -15
  154. package/dist/pwa/routes/rewards-auto-downgrade.js +9 -7
  155. package/dist/pwa/routes/rewards-escrow-expire.js +7 -5
  156. package/dist/pwa/routes/rfqs.js +163 -85
  157. package/dist/pwa/routes/search.js +16 -14
  158. package/dist/pwa/routes/secondhand.js +25 -22
  159. package/dist/pwa/routes/seller-quota.js +24 -26
  160. package/dist/pwa/routes/share-redirects.js +59 -55
  161. package/dist/pwa/routes/shareables-interactions.js +34 -35
  162. package/dist/pwa/routes/shareables.js +55 -51
  163. package/dist/pwa/routes/shop-referral.js +57 -0
  164. package/dist/pwa/routes/shops.js +20 -18
  165. package/dist/pwa/routes/signaling.js +10 -9
  166. package/dist/pwa/routes/skill-market.js +16 -16
  167. package/dist/pwa/routes/skills.js +15 -14
  168. package/dist/pwa/routes/snf.js +14 -13
  169. package/dist/pwa/routes/tags.js +10 -9
  170. package/dist/pwa/routes/task-proposals.js +45 -0
  171. package/dist/pwa/routes/trial.js +69 -51
  172. package/dist/pwa/routes/trusted-kpi.js +20 -18
  173. package/dist/pwa/routes/url-claim.js +67 -28
  174. package/dist/pwa/routes/users-public.js +62 -60
  175. package/dist/pwa/routes/variants.js +12 -13
  176. package/dist/pwa/routes/verifier-user.js +61 -21
  177. package/dist/pwa/routes/verify-tasks.js +49 -25
  178. package/dist/pwa/routes/waitlist.js +16 -15
  179. package/dist/pwa/routes/wallet-read.js +74 -36
  180. package/dist/pwa/routes/wallet-write.js +12 -9
  181. package/dist/pwa/routes/webauthn.js +25 -26
  182. package/dist/pwa/routes/webhooks.js +26 -26
  183. package/dist/pwa/routes/welcome.js +45 -50
  184. package/dist/pwa/routes/wishlist-qa.js +29 -32
  185. package/dist/pwa/server.js +237 -81
  186. package/dist/version.js +1 -1
  187. package/package.json +47 -2
@@ -20,6 +20,7 @@ import path from 'path';
20
20
  import { fileURLToPath } from 'url';
21
21
  import crypto from 'crypto';
22
22
  import { initDatabase, generateId } from '../layer0-foundation/L0-1-database/schema.js';
23
+ import { setSeamDb } from '../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
23
24
  import { initSystemUser, transition, getOrderStatus, checkTimeouts, settleFault } from '../layer0-foundation/L0-2-state-machine/engine.js';
24
25
  import { endpointToAction, endpointToReadAction } from './endpoint-actions.js';
25
26
  import { AGENT_RATE_PER_MIN_DEFAULTS, CROSS_USER_READ_DAILY_CAP, MASS_ACTION_TYPES, MASS_ACTION_DAILY_CAPS } from './limits.js';
@@ -48,6 +49,7 @@ import { createHmac, createHash, randomBytes, scryptSync, timingSafeEqual } from
48
49
  import { safeFetch } from './security/ssrf.js';
49
50
  // @simplewebauthn/server 已迁出到 src/pwa/routes/webauthn.ts (#1013 Phase 1)
50
51
  import { registerWebauthnRoutes } from './routes/webauthn.js';
52
+ import { createHumanPresence } from './human-presence.js';
51
53
  // welcome 域(#991 /welcome 落地页 + #1005 反 bot)已迁出 (#1013 Phase 2)
52
54
  import { registerWelcomeRoutes } from './routes/welcome.js';
53
55
  // 测评免单 (#978-#988) 域 + 评估 cron 已迁出 (#1013 Phase 3)
@@ -168,6 +170,7 @@ import { registerListingsRoutes } from './routes/listings.js';
168
170
  import { registerEvidenceRoutes } from './routes/evidence.js';
169
171
  // 分享 / 重定向 / QR (#1013 Phase 54) — 4 endpoints
170
172
  import { registerShareRedirectsRoutes } from './routes/share-redirects.js';
173
+ import { registerShopReferralRoutes } from './routes/shop-referral.js';
171
174
  // Profile 凭据 (#1013 Phase 55) — 5 endpoints (密码 + 邮箱绑定)
172
175
  import { registerProfileCredentialsRoutes } from './routes/profile-credentials.js';
173
176
  // Profile 双轨挂靠 (#1013 Phase 56) — 3 endpoints
@@ -303,9 +306,19 @@ import { registerAuthRegisterRoutes } from './routes/auth-register.js';
303
306
  import { registerBuildFeedbackRoutes } from './routes/build-feedback.js';
304
307
  import { initBuildFeedbackSchema } from '../layer2-business/L2-8-feedback/build-feedback-engine.js';
305
308
  import { registerBuildTasksRoutes } from './routes/build-tasks.js';
309
+ import { registerPublicBuildTasksRoutes } from './routes/public-build-tasks.js';
306
310
  import { initBuildTasksSchema } from '../layer2-business/L2-9-contribution/build-tasks-engine.js';
311
+ import { initBuildTaskAgentMetadataSchema } from '../layer2-business/L2-9-contribution/build-task-agent-metadata-store.js';
312
+ import { initTaskProposalSchema } from '../layer2-business/L2-9-contribution/task-proposal-store.js';
313
+ import { registerTaskProposalsRoutes } from './routes/task-proposals.js';
314
+ import { createSlidingWindowLimiter } from './rate-limit.js';
307
315
  import { registerBuildReputationRoutes } from './routes/build-reputation.js';
308
316
  import { initBuildReputationSchema } from '../layer2-business/L2-9-contribution/build-reputation-engine.js';
317
+ import { initGithubCredentialStoreSchema } from '../layer2-business/L2-9-contribution/github-credential-store.js';
318
+ import { initIdentityBindingSchema } from '../layer2-business/L2-9-contribution/identity-binding-store.js';
319
+ import { initIdentityClaimChallengeSchema } from '../layer2-business/L2-9-contribution/identity-claim-challenge-store.js';
320
+ import { registerContributionIdentityRoutes } from './routes/contribution-identity.js';
321
+ import { registerContributionScoreRoutes } from './routes/contribution-score.js';
309
322
  const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
310
323
  // ─── 链上地址派生 ──────────────────────────────────────────────
311
324
  const MASTER_SEED = process.env.WALLET_MASTER_SEED ?? 'webaz-dev-seed-changeme';
@@ -344,6 +357,7 @@ function deriveDepositAddress(userId) {
344
357
  }
345
358
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
346
359
  const db = initDatabase();
360
+ setSeamDb(db); // RFC-016 Phase 0:注入异步 DB seam(本进程)
347
361
  // #1013 Phase 9: claim-verify helpers 预绑 db(routes/claim-verify.ts 已 export 多签名版本)
348
362
  // 让 product-claims / review-claims / etc 跨域调用零侵入(无需改 callsite signature)
349
363
  const isEligibleClaimVerifier = (userId) => isEligibleClaimVerifierRaw(db, userId);
@@ -359,7 +373,12 @@ initReputationSchema(db);
359
373
  initOrderChainSchema(db);
360
374
  initBuildFeedbackSchema(db); // RFC-004 build_feedback
361
375
  initBuildTasksSchema(db); // RFC-006 build_tasks(协调层)
376
+ initBuildTaskAgentMetadataSchema(db); // PR9B — agent-ready task metadata satellite(schema only;FUTURE-TASK-BOARD-V1-DESIGN #326)
377
+ initTaskProposalSchema(db); // Task Proposal Inbox v1 — suggestion inbox(maintainer review;never auto build_task)
362
378
  initBuildReputationSchema(db); // RFC-006 build_reputation(独立池 + 贡献者看板)
379
+ initGithubCredentialStoreSchema(db); // PR 3B-3a — GitHub credential store + RFC-017 fact layer (schema only)
380
+ initIdentityBindingSchema(db); // PR 4a — GitHub identity → WebAZ account binding (append-only events + active projection)
381
+ initIdentityClaimChallengeSchema(db); // PR-F1 — identity-claim publication-challenge state (server-side nonce hash; schema only)
363
382
  initSnfSchema(db);
364
383
  initExternalAnchorSchema(db);
365
384
  // 启动时检查月衰减(last_decay_at ≥25 天才触发,重启幂等)
@@ -629,6 +648,53 @@ try {
629
648
  db.exec('CREATE INDEX IF NOT EXISTS idx_psa_recipient ON product_share_attribution(recipient_id, product_id)');
630
649
  }
631
650
  catch { }
651
+ // provenance (additive, audit-only — does NOT change commission math): how this attribution was created.
652
+ // source_type = 'direct_share'(商品/笔记直接分享) | 'shop_referral_verified_purchase'(店铺推荐懒升级)
653
+ try {
654
+ db.exec("ALTER TABLE product_share_attribution ADD COLUMN source_type TEXT");
655
+ }
656
+ catch { }
657
+ try {
658
+ db.exec("ALTER TABLE product_share_attribution ADD COLUMN source_ref TEXT");
659
+ }
660
+ catch { }
661
+ try {
662
+ db.exec("ALTER TABLE product_share_attribution ADD COLUMN source_shop_seller_id TEXT");
663
+ }
664
+ catch { }
665
+ try {
666
+ db.exec("ALTER TABLE product_share_attribution ADD COLUMN source_qualified_order_id TEXT");
667
+ }
668
+ catch { }
669
+ // ─── 店铺推荐锚定 (shop_referral_attribution) ─────────────────
670
+ // 店铺推荐【只】锚定推荐关系 + 二叉树位置 + 店铺来源,first-touch 30 天锁;它【不是】全店佣金权。
671
+ // 仅当被推荐人后来真实下单店铺里的某商品、且推荐人自己也 completed 买过同款时,才在下单时被【懒升级】
672
+ // 为该商品的 product_share_attribution(见 orders-create maybePromoteShopReferralToProductAttribution)。
673
+ db.exec(`
674
+ CREATE TABLE IF NOT EXISTS shop_referral_attribution (
675
+ seller_id TEXT NOT NULL,
676
+ recipient_id TEXT NOT NULL,
677
+ referrer_id TEXT NOT NULL,
678
+ ref_code TEXT NOT NULL,
679
+ side TEXT,
680
+ created_at TEXT DEFAULT (datetime('now')),
681
+ expires_at TEXT NOT NULL,
682
+ source TEXT DEFAULT 'shop_referral',
683
+ PRIMARY KEY (seller_id, recipient_id)
684
+ )
685
+ `);
686
+ try {
687
+ db.exec('CREATE INDEX IF NOT EXISTS idx_sra_referrer ON shop_referral_attribution(referrer_id, seller_id)');
688
+ }
689
+ catch { }
690
+ try {
691
+ db.exec('CREATE INDEX IF NOT EXISTS idx_sra_recipient ON shop_referral_attribution(recipient_id)');
692
+ }
693
+ catch { }
694
+ try {
695
+ db.exec('CREATE INDEX IF NOT EXISTS idx_sra_expires ON shop_referral_attribution(expires_at)');
696
+ }
697
+ catch { }
632
698
  // 商品分享链反推:buyer 买商品 P 时 → L1=谁分享 P 给 buyer → L2=谁分享 P 给 L1 → ...
633
699
  // 与 sponsor_path / placement_path 完全无关。某层断链 → 该层 null(佣金回流协议池)。
634
700
  function getProductShareChain(productId, buyerId, depth = 3) {
@@ -792,7 +858,9 @@ const DEFAULT_PARAMS = [
792
858
  // RFC-008:fund_base 硬帽 1%;pre-launch 减免到 0(社区基金按真实 GMV 注入,0 GMV 时是无回报的税)。有真实 GMV 再由治理开启(≤1%)。
793
859
  { key: 'fund_base_rate', value: '0', type: 'number', description: '协议基金池基础费率(RFC-008 硬帽 1%;pre-launch 减免=0,有真实 GMV 再由治理开启 ≤1%)', category: 'fee', min: 0, max: 0.01 },
794
860
  // RFC-008:起步免赔付门槛。0 = bootstrap(新商家零质押、违约免赔付只退款+掉信誉,降进入门槛);1 = 要求卖家质押(下单锁 stake、违约真没收)。上轨道后由治理开启。
795
- { key: 'require_seller_stake', value: '0', type: 'number', description: 'RFC-008 是否要求卖家质押(0=起步免赔付/零门槛,1=要求质押/真没收)', category: 'fee', min: 0, max: 1 },
861
+ // ⚠️ Codex #111:stake-required 模式(=1)【尚未实现】—— 下单不锁 stake、settleFault 仍按 stake_backing=0 不没收,
862
+ // 开启会给出虚假"真没收"协议语义。故 max 锁 0(不可开启);待 Phase 3 钱路径迁移实现真锁(下单原子锁 balance→staked)再放开。
863
+ { key: 'require_seller_stake', value: '0', type: 'number', description: 'RFC-008 是否要求卖家质押(0=起步免赔付/零门槛)。⚠️ stake-required(=1)未实现、暂锁 0 不可开启,见 Phase 3', category: 'fee', min: 0, max: 0 },
796
864
  // RFC-008 stage 2:违约罚没率,【与质押率解耦】(低质押=低摩擦 + 高罚没=强威慑,单一费率做不到)。
797
865
  // 背书订单:penalty = fault_penalty_rate × total,先扣 staked(封顶背书)再扣自由 balance(责任自负,真可执行)。
798
866
  // 起步免赔付(stake_backing=0):仍 0 没收,绝不碰新商家自由余额。settleFault 按订单 stake_backing 判定。
@@ -828,11 +896,14 @@ const DEFAULT_PARAMS = [
828
896
  { key: 'require_human_presence_for_arbitrate', value: '1', type: 'number', description: 'Arbitrator 仲裁需 WebAuthn 一次性 token(1=强制 / 0=不强制)— spec §4 铁律', category: 'security', min: 0, max: 1 },
829
897
  { key: 'require_human_presence_for_agent_revoke', value: '1', type: 'number', description: '用户撤销 agent 需 WebAuthn 一次性 token — spec §4 铁律', category: 'security', min: 0, max: 1 },
830
898
  { key: 'require_human_presence_for_delete_passkey', value: '1', type: 'number', description: '删除 Passkey 自身需 WebAuthn 一次性 token — 防失窃 Passkey 不需 Passkey 就可删它,堵死自我无效化漏洞', category: 'security', min: 0, max: 1 },
899
+ { key: 'require_human_presence_for_identity_claim', value: '1', type: 'number', description: 'GitHub 身份认领绑定(claim commit)需 WebAuthn 一次性 token — 4b 身份认领的真人铁律门(PR-F0 plumbing;claim endpoint 尚未开放)。默认强制,与 vote/arbitrate/agent_revoke/delete_passkey 同级。', category: 'security', min: 0, max: 1 },
831
900
  { key: 'require_human_presence_for_governance_apply', value: '1', type: 'number', description: '治理岗位申请(apply)需 WebAuthn 一次性 token — spec §3.1 Iron-Rule 反诱导 + 真人门', category: 'security', min: 0, max: 1 },
832
901
  { key: 'require_human_presence_for_governance_activate', value: '1', type: 'number', description: 'maintainer 激活治理岗位(activate)需 WebAuthn 一次性 token — spec §4.4 Iron-Rule 真人签发', category: 'security', min: 0, max: 1 },
833
902
  { key: 'require_human_presence_for_governance_resign', value: '1', type: 'number', description: '主动卸任治理岗位(resign)需 WebAuthn 一次性 token — spec §6.1 二次验证', category: 'security', min: 0, max: 1 },
834
903
  { key: 'require_human_presence_for_governance_appeal_resolve', value: '1', type: 'number', description: 'maintainer 裁决申诉(resolve appeal)需 WebAuthn 一次性 token — spec §7.2 Iron-Rule', category: 'security', min: 0, max: 1 },
835
- { key: 'require_human_presence_for_withdraw', value: '1', type: 'number', description: '提现(资金转出)需 WebAuthn 一次性 token 资金转出=真人在场铁律,email-OTP 在 agent 威胁模型下不足(agent 可读收件箱);#1115 全额对齐', category: 'security', min: 0, max: 1 },
904
+ // ⚠️ Codex #100:提现真人在场是【铁律】,锁死 min=max=1 不可关闭( protocol admin PATCH 0 绕过)
905
+ // 且 wallet-write.ts 已【无条件】执行 Passkey gate(不读此 param),此处仅作诚实展示 + PATCH 防线。
906
+ { key: 'require_human_presence_for_withdraw', value: '1', type: 'number', description: '提现(资金转出)需 WebAuthn 一次性 token — 真人在场【铁律,锁死不可关闭】;wallet-write 无条件执行', category: 'security', min: 1, max: 1 },
836
907
  { key: 'governance_resign_cooldown_days', value: '30', type: 'number', description: '主动卸任后冷却期(天)— 防止 farming 切换洗票 / 误操作反复', category: 'governance', min: 7, max: 365 },
837
908
  { key: 'governance_appeal_window_days', value: '14', type: 'number', description: '收到 auto_deactivate 通知后申诉窗口(天)— spec §7.2', category: 'governance', min: 7, max: 90 },
838
909
  { key: 'governance_appeal_min_reason_chars', value: '100', type: 'number', description: '申诉理由最少字符数(防空 appeal)', category: 'governance', min: 30, max: 2000 },
@@ -941,6 +1012,24 @@ try {
941
1012
  WHERE key = 'fund_base_rate' AND max_value > 0.01`).run();
942
1013
  if (fbCap.changes > 0)
943
1014
  console.log(`[migration RFC-008] fund_base 硬帽收紧 → max 1%`);
1015
+ // Codex #112 P1:仅收紧 max_value 不够 —— 历史 value > 新 cap 的行(如曾被治理调到 0.05)在 runtime
1016
+ // getProtocolParam 直接读 value,仍按超帽费率收费,硬帽形同虚设。逐 key 把超帽 value clamp 回 cap,
1017
+ // 并记 protocol_params_log。先于下面 fund_base 的 pre-launch 减免(减免只针对未被治理改过的原始 0.01)。
1018
+ const clampFeeValue = (key, cap) => {
1019
+ const cur = db.prepare('SELECT value FROM protocol_params WHERE key = ? AND CAST(value AS REAL) > ?').get(key, cap);
1020
+ if (!cur)
1021
+ return;
1022
+ db.prepare(`UPDATE protocol_params SET value = ?, updated_at = datetime('now') WHERE key = ? AND CAST(value AS REAL) > ?`).run(String(cap), key, cap);
1023
+ console.log(`[migration RFC-008] ${key} value ${cur.value} → ${cap}(clamp 回硬帽)`);
1024
+ try {
1025
+ db.prepare(`INSERT INTO protocol_params_log (id, key, old_value, new_value, changed_by, action)
1026
+ VALUES (?,?,?,?,?,'migrate')`).run(generateId('ppl'), key, cur.value, String(cap), 'migration_RFC-008');
1027
+ }
1028
+ catch { }
1029
+ };
1030
+ clampFeeValue('protocol_fee_rate_shop', 0.02);
1031
+ clampFeeValue('protocol_fee_rate_secondhand', 0.02);
1032
+ clampFeeValue('fund_base_rate', 0.01);
944
1033
  const fb = db.prepare(`UPDATE protocol_params SET value = '0', default_value = '0', updated_at = datetime('now')
945
1034
  WHERE key = 'fund_base_rate' AND value = '0.01' AND updated_by IS NULL`).run();
946
1035
  if (fb.changes > 0) {
@@ -955,6 +1044,52 @@ try {
955
1044
  catch (e) {
956
1045
  console.error('[migration RFC-008]', e);
957
1046
  }
1047
+ // Codex #111 P1:require_seller_stake 当前是【假开关】—— 即使=1,下单仍写 stake_backing=0、不锁 stake,
1048
+ // settleFault 仍按 backing=0 不没收;开启只会给出虚假"真没收"语义。stake-required 真锁留待 Phase 3。
1049
+ // 在此之前:max 锁 0(不可开启)+ 若历史 DB 被设为 1 则降回 0(中和假开关),并记 protocol_params_log。
1050
+ try {
1051
+ const cap = db.prepare(`UPDATE protocol_params SET max_value = 0, updated_at = datetime('now')
1052
+ WHERE key = 'require_seller_stake' AND max_value > 0`).run();
1053
+ if (cap.changes > 0)
1054
+ console.log(`[migration RFC-008] require_seller_stake max → 0(stake-required 未实现,锁关)`);
1055
+ const rss = db.prepare(`SELECT value FROM protocol_params WHERE key = 'require_seller_stake' AND CAST(value AS REAL) > 0`).get();
1056
+ if (rss) {
1057
+ db.prepare(`UPDATE protocol_params SET value = '0', updated_at = datetime('now') WHERE key = 'require_seller_stake'`).run();
1058
+ console.log(`[migration RFC-008] require_seller_stake value ${rss.value} → 0(假开关中和)`);
1059
+ try {
1060
+ db.prepare(`INSERT INTO protocol_params_log (id, key, old_value, new_value, changed_by, action)
1061
+ VALUES (?,?,?,?,?,'migrate')`).run(generateId('ppl'), 'require_seller_stake', rss.value, '0', 'migration_RFC-008');
1062
+ }
1063
+ catch { }
1064
+ }
1065
+ }
1066
+ catch (e) {
1067
+ console.error('[migration require_seller_stake lock]', e);
1068
+ }
1069
+ // Codex #100 P1:提现真人 Passkey 是【铁律】,绝不可被 protocol param 关闭。
1070
+ // 旧默认 min=0/max=1 让 protocol admin PATCH 设 0 即可绕过(wallet-write 旧代码 if(param===1))。
1071
+ // 双重防线:wallet-write 已改无条件执行 Passkey gate(不再读此 param);此处把 param 锁死 value/min/max=1
1072
+ // (PATCH 校验 min/max → 再不能设 0),并把历史 DB 的 value=0 / 放开的 min/max clamp 回 1,记 protocol_params_log。
1073
+ try {
1074
+ const wkey = 'require_human_presence_for_withdraw';
1075
+ const lockBounds = db.prepare(`UPDATE protocol_params SET min_value = 1, max_value = 1, updated_at = datetime('now')
1076
+ WHERE key = ? AND (min_value != 1 OR max_value != 1)`).run(wkey);
1077
+ if (lockBounds.changes > 0)
1078
+ console.log(`[migration Codex#100] ${wkey} min/max → 1(铁律,锁死不可关闭)`);
1079
+ const hp = db.prepare(`SELECT value FROM protocol_params WHERE key = ? AND CAST(value AS REAL) != 1`).get(wkey);
1080
+ if (hp) {
1081
+ db.prepare(`UPDATE protocol_params SET value = '1', default_value = '1', updated_at = datetime('now') WHERE key = ?`).run(wkey);
1082
+ console.log(`[migration Codex#100] ${wkey} value ${hp.value} → 1(提现真人铁律,历史绕过值 clamp 回)`);
1083
+ try {
1084
+ db.prepare(`INSERT INTO protocol_params_log (id, key, old_value, new_value, changed_by, action)
1085
+ VALUES (?,?,?,?,?,'migrate')`).run(generateId('ppl'), wkey, hp.value, '1', 'migration_Codex-100');
1086
+ }
1087
+ catch { }
1088
+ }
1089
+ }
1090
+ catch (e) {
1091
+ console.error('[migration require_human_presence_for_withdraw lock]', e);
1092
+ }
958
1093
  // Wave G-2: USDC ↔ WAZ 转换助手
959
1094
  function usdcToWaz(usdc) {
960
1095
  const rate = getProtocolParam('waz_usdc_rate', 1.0);
@@ -982,6 +1117,10 @@ function getProtocolParam(key, fallback) {
982
1117
  return fallback;
983
1118
  }
984
1119
  }
1120
+ // PR-F0: 人工铁律 gate(consumeGateToken / requireHumanPresence)抽到 ./human-presence.ts 以便单测
1121
+ // (behavior-zero)。必须在【首个使用点】(下方 arbitrate/vote/claim-verify/webauthn 等路由注册)之前
1122
+ // 实例化 —— 故置于 db + getProtocolParam 定义之后。原为 hoisted 函数声明,现为工厂返回的 const。
1123
+ const { consumeGateToken, requireHumanPresence } = createHumanPresence(db, getProtocolParam);
985
1124
  // Wave E-4 audit P1-3: 平台奖励发放审计 — 记录所有 platform → user 的免费 WAZ 拨付
986
1125
  db.exec(`
987
1126
  CREATE TABLE IF NOT EXISTS platform_reward_log (
@@ -3338,6 +3477,23 @@ function resolveUserRef(raw) {
3338
3477
  }
3339
3478
  return null;
3340
3479
  }
3480
+ // Invite-code-ONLY resolver — for registration sponsor + /i short links. Accepts a 6-7 char permanent_code
3481
+ // with an optional -L/-R side suffix; rejects usr_xxx / @handle / bare handle (anti-ambiguity, narrows the
3482
+ // public invite surface). Excludes sys_protocol + the internal auditor. Distinct from resolveUserRef, which
3483
+ // stays for personal-page / non-invite lookups.
3484
+ function resolveInviteCodeRef(raw) {
3485
+ if (!raw || typeof raw !== 'string')
3486
+ return null;
3487
+ const m = raw.trim().match(/^([A-Za-z0-9]{6,7})(?:-([LRlr]))?$/);
3488
+ if (!m)
3489
+ return null;
3490
+ const code = m[1].toUpperCase();
3491
+ const side = m[2] ? (m[2].toLowerCase() === 'l' ? 'left' : 'right') : null;
3492
+ const r = db.prepare("SELECT id FROM users WHERE permanent_code = ? AND id NOT IN ('sys_protocol', ?) LIMIT 1").get(code, INTERNAL_AUDITOR_ID);
3493
+ if (!r)
3494
+ return null;
3495
+ return { userId: r.id, code, side };
3496
+ }
3341
3497
  // 一次性回填:现有用户补齐 permanent_code + handle(启动时跑)
3342
3498
  try {
3343
3499
  const rows = db.prepare("SELECT id, name FROM users WHERE permanent_code IS NULL OR handle IS NULL").all();
@@ -5687,7 +5843,7 @@ function isTrustedRole(user) {
5687
5843
  // #1013 Phase 118: register 已迁出(VALID_REGIONS 在下方 const → 用 getter 避免 TDZ)
5688
5844
  registerAuthRegisterRoutes(app, {
5689
5845
  db, errorRes, INTERNAL_AUDITOR_ID,
5690
- isAllowedSponsor, resolveUserRef,
5846
+ isAllowedSponsor, resolveUserRef, resolveInviteCodeRef,
5691
5847
  generateId, generateSecureKey, generatePermanentCode, deriveHandle,
5692
5848
  clientIpHash, clientUaHash,
5693
5849
  get VALID_REGIONS() { return VALID_REGIONS; },
@@ -5718,8 +5874,28 @@ registerBuildTasksRoutes(app, {
5718
5874
  db, auth,
5719
5875
  requireSupportAdmin: (req, res) => requireAdminPermission(req, res, 'support'),
5720
5876
  });
5877
+ // PR9C-1 — public Task Board read surface(无需登录;仅 audience=public + status=open;只读,带 value_boundary)
5878
+ registerPublicBuildTasksRoutes(app, { db, errorRes });
5879
+ // Task Proposal Inbox v1 — public submit(匿名,validated,限流+去重)+ admin review;建议入收件箱,绝不自动成正式任务/上公开板
5880
+ const proposalRateLimiter = createSlidingWindowLimiter(20, 3600_000); // 20 submissions / hour / IP
5881
+ registerTaskProposalsRoutes(app, {
5882
+ db, errorRes,
5883
+ requireSupportAdmin: (req, res) => requireAdminPermission(req, res, 'support'),
5884
+ rateLimitOk: (key) => proposalRateLimiter(key),
5885
+ });
5721
5886
  // RFC-006 Gap 2:贡献者自查看板(build_reputation 独立池)
5722
5887
  registerBuildReputationRoutes(app, { db, auth });
5888
+ // PR-F3c — 最小 GitHub 身份认领 API(发起挑战 → Passkey 人门 + WebAZ 自验 gist → F2 原子认领)。
5889
+ // GitHub 读 token 仅来自可信服务端配置;未配置则 completion fail-closed(不做匿名限流读)。
5890
+ registerContributionIdentityRoutes(app, {
5891
+ auth,
5892
+ requireHumanPresence,
5893
+ errorRes,
5894
+ getGithubReadToken: () => process.env.GITHUB_CONTRIB_READ_TOKEN || undefined,
5895
+ });
5896
+ // PR5F — Contribution Score v1 evidence READ surface (logged-in self-view; read-only, no score).
5897
+ // Returns the caller's OWN component evidence wrapped in the PR5A uncommitted-value boundary.
5898
+ registerContributionScoreRoutes(app, { auth, errorRes });
5723
5899
  // #1013 Phase 48: 3 auth/sessions endpoints 已迁出到 routes/auth-sessions.ts
5724
5900
  registerAuthSessionsRoutes(app, { db, auth, verifyPassword, recordSession, generateSecureKey });
5725
5901
  // 个人资料:查看 API Key + 联系方式
@@ -7037,9 +7213,9 @@ setInterval(() => {
7037
7213
  }, 24 * 60 * 60_000);
7038
7214
  // 2026-05-24 #980:测评免单 reach 评估 cron — 每 6h 跑一次
7039
7215
  // #1013 Phase 3: evaluateTrialClaims + /api/admin/trial/run-eval 已迁出到 routes/trial.ts;这里只挂 cron
7040
- setInterval(() => {
7216
+ setInterval(async () => {
7041
7217
  try {
7042
- const r = evaluateTrialClaims(db, generateId);
7218
+ const r = await evaluateTrialClaims(db, generateId);
7043
7219
  if (r.evaluated > 0)
7044
7220
  console.log(`[cron trial-eval] evaluated=${r.evaluated} refunded=${r.refunded} expired=${r.expired}`);
7045
7221
  }
@@ -7070,7 +7246,7 @@ registerDashboardsRoutes(app, { db, auth });
7070
7246
  // #1013 Phase 56: 3 placement endpoints 已迁出到 routes/profile-placement.ts
7071
7247
  registerProfilePlacementRoutes(app, {
7072
7248
  db, auth, internalAuditorId: INTERNAL_AUDITOR_ID,
7073
- resolveUserRef, pickPreferredSide, joinPowerLeg,
7249
+ resolveUserRef, resolveInviteCodeRef, pickPreferredSide, joinPowerLeg,
7074
7250
  });
7075
7251
  // shares/dashboard — Phase 110 已迁出
7076
7252
  // ─── 推土机轨道:推广统计端点 ─────────────────────────────────
@@ -7364,12 +7540,12 @@ registerRewardsApplyRoutes(app, {
7364
7540
  });
7365
7541
  // task #1093 stage 5: admin manual auto-deactivate sweep trigger
7366
7542
  // Useful for ops + testing. The scheduled cron also runs every N hours.
7367
- app.post('/api/admin/governance/run-auto-deactivate', (req, res) => {
7543
+ app.post('/api/admin/governance/run-auto-deactivate', async (req, res) => {
7368
7544
  const admin = requireAdminPermission(req, res, 'arbitration');
7369
7545
  if (!admin)
7370
7546
  return;
7371
7547
  try {
7372
- const result = runAutoDeactivateSweep({ db, generateId, getProtocolParam });
7548
+ const result = await runAutoDeactivateSweep({ db, generateId, getProtocolParam });
7373
7549
  logAdminAction(admin.id, 'governance_auto_deactivate_sweep', null, null, {
7374
7550
  scanned: result.scanned,
7375
7551
  deactivated_count: result.deactivated.length,
@@ -7530,7 +7706,7 @@ registerOrdersCreateRoutes(app, {
7530
7706
  getActiveFlashSale, applyCouponToOrder, getProtocolParam,
7531
7707
  getProductShareChain, isAllowedSponsor, checkStockAndMaybeDelist, auditSponsorChainCross,
7532
7708
  appendOrderEvent, transition, notifyTransition, shouldAutoAccept, ensureCharityRep,
7533
- broadcastSystemEvent,
7709
+ broadcastSystemEvent, resolveInviteCodeRef,
7534
7710
  signPassport: (message) => privateKeyToAccount(derivePrivKey('platform-hot-wallet')).signMessage({ message }),
7535
7711
  issuerAddress: () => privateKeyToAddress(derivePrivKey('platform-hot-wallet')),
7536
7712
  });
@@ -9336,69 +9512,8 @@ registerWebauthnRoutes(app, {
9336
9512
  invalidateAgentRiskCacheForUser,
9337
9513
  requireHumanPresence, // #1044 — DELETE passkey 自身需 token
9338
9514
  });
9339
- // 验证 gate token:被业务端点(如 /api/wallet/withdraw)消费
9340
- // M-1: CAS 先抢占性 UPDATE,只有 changes=1 才认为本次成功消费;
9341
- // 然后再读 row 校验 user/purpose/业务字段。多副本部署也安全。
9342
- function consumeGateToken(userId, token, purpose, validate) {
9343
- if (!token)
9344
- return { ok: false, reason: '缺少 X-WebAuthn-Token' };
9345
- // 先抢占:未消费 + 未过期 才能 mark consumed
9346
- const claim = db.prepare(`UPDATE webauthn_gate_tokens
9347
- SET consumed_at = datetime('now')
9348
- WHERE id = ? AND consumed_at IS NULL AND expires_at > datetime('now')`).run(token);
9349
- if (claim.changes !== 1) {
9350
- // 抢占失败的两种原因区分(仅用于 reason 文案)
9351
- const exist = db.prepare('SELECT consumed_at FROM webauthn_gate_tokens WHERE id = ?').get(token);
9352
- if (!exist)
9353
- return { ok: false, reason: 'token 不存在' };
9354
- if (exist.consumed_at)
9355
- return { ok: false, reason: 'token 已使用' };
9356
- return { ok: false, reason: 'token 已过期' };
9357
- }
9358
- // 已抢占 → 读 row 校验 user/purpose/业务字段;若校验失败 token 仍然作废(防止枚举攻击下的重试)
9359
- const row = db.prepare(`SELECT user_id, purpose, purpose_data FROM webauthn_gate_tokens WHERE id = ?`)
9360
- .get(token);
9361
- if (row.user_id !== userId)
9362
- return { ok: false, reason: 'token 用户不匹配' };
9363
- if (row.purpose !== purpose)
9364
- return { ok: false, reason: 'token 用途不匹配' };
9365
- let data = null;
9366
- try {
9367
- data = row.purpose_data ? JSON.parse(row.purpose_data) : null;
9368
- }
9369
- catch { }
9370
- if (!validate(data))
9371
- return { ok: false, reason: 'token 业务参数不匹配' };
9372
- return { ok: true };
9373
- }
9374
- // ─── 2026-05-23 Agent 治理铁律:人工铁律节点 ───────────────────
9375
- // spec: docs/AGENT-GOVERNANCE.md §4
9376
- // 关键节点(verifier 投票 / arbitrator 仲裁)必须真实人工参与,agent 代操作要被拦截。
9377
- // 实现:要求 webauthn_gate_token(一次性消费 · 60s 内有效)+ 协议参数开关。
9378
- // 2026-05-25 #1006:默认值 0 → 1 启用强制;is_system fixture 旁路只对 vote/arbitrate 生效。
9379
- // agent_revoke 是普通用户操作,不享有 is_system 豁免(无 fixture 概念)。
9380
- function requireHumanPresence(userId, purpose, token, paramKey, validate = () => true) {
9381
- const enabled = Number(getProtocolParam(paramKey, 1)) === 1;
9382
- if (!enabled)
9383
- return { ok: true }; // 协议参数关闭 → 不强制
9384
- // is_system fixture 旁路:用于 E2E 测试 / 自动化 / migration 账号
9385
- // 仅 vote / arbitrate 适用(这两类有白名单表 + is_system 列);真实用户无此豁免
9386
- if (purpose === 'vote') {
9387
- const wl = db.prepare('SELECT is_system FROM verifier_whitelist WHERE user_id = ?').get(userId);
9388
- if (wl?.is_system === 1)
9389
- return { ok: true };
9390
- }
9391
- else if (purpose === 'arbitrate') {
9392
- const wl = db.prepare('SELECT is_system FROM arbitrator_whitelist WHERE user_id = ?').get(userId);
9393
- if (wl?.is_system === 1)
9394
- return { ok: true };
9395
- }
9396
- const result = consumeGateToken(userId, token, purpose, validate);
9397
- if (!result.ok) {
9398
- return { ok: false, error_code: 'HUMAN_PRESENCE_REQUIRED', reason: result.reason || '此操作需真实人工 WebAuthn 验证', required_when_enabled: true };
9399
- }
9400
- return { ok: true };
9401
- }
9515
+ // consumeGateToken / requireHumanPresence 已抽出到 ./human-presence.ts(PR-F0,behavior-zero,
9516
+ // 工厂 createHumanPresence(db, getProtocolParam),在 db+getProtocolParam 定义后即实例化 —— 见上方)。
9402
9517
  // ─── M7.3 claim 验证任务系统 ──────────────────────────────
9403
9518
  // #1013 Phase 9: 8 endpoints + 三路径结算 + outlier strike + 铁律 §4 已迁出到 routes/claim-verify.ts
9404
9519
  // product_claim_tasks (Sprint 1) 跨域用 isEligibleClaimVerifier — 从 import 拿
@@ -9706,7 +9821,8 @@ registerReviewsRoutes(app, {
9706
9821
  // #1013 Phase 76: 3 垂类 × 2 (POST claim + GET claims) = 6 endpoints 已迁出
9707
9822
  registerClaimInitiatorsRoutes(app, { db, auth, isTrustedRole, errorRes, generateId });
9708
9823
  // ─── 分享 / 重定向 / QR (#1013 Phase 54) ────────────────────
9709
- registerShareRedirectsRoutes(app, { db, auth, clientIpHash, clientUaHash });
9824
+ registerShareRedirectsRoutes(app, { db, auth, clientIpHash, clientUaHash, resolveInviteCodeRef });
9825
+ registerShopReferralRoutes(app, { db, auth, errorRes, internalAuditorId: INTERNAL_AUDITOR_ID, resolveUserRef, resolveInviteCodeRef });
9710
9826
  // 慈善许愿池 API
9711
9827
  // ============================================================
9712
9828
  // ─── 慈善许愿池 (charity) ─────────────────────────────────
@@ -9824,14 +9940,11 @@ function runEnforcement() {
9824
9940
  console.error('证据清理失败:', e.message);
9825
9941
  }
9826
9942
  // Phase C 笔记图片孤儿 cleanup — 没有任何笔记引用且超过 1 小时 grace 期的 blob
9827
- try {
9828
- const npCleaned = cleanupOrphanNotePhotos(db);
9829
- if (npCleaned.swept > 0)
9830
- console.log(`⚡ Note photo cleanup × ${npCleaned.swept} files (${Math.round(npCleaned.bytes / 1024)}KB)`);
9831
- }
9832
- catch (e) {
9833
- console.error('笔记图片清理失败:', e.message);
9834
- }
9943
+ // RFC-016:cleanupOrphanNotePhotos 已异步(纯读 + fs 删);best-effort 孤儿清理,fire-and-forget 不阻塞同步 cron runEnforcement。
9944
+ cleanupOrphanNotePhotos(db)
9945
+ .then(npCleaned => { if (npCleaned.swept > 0)
9946
+ console.log(`⚡ Note photo cleanup × ${npCleaned.swept} files (${Math.round(npCleaned.bytes / 1024)}KB)`); })
9947
+ .catch(e => console.error('笔记图片清理失败:', e.message));
9835
9948
  // E1 流量口令 reclaim — retired 满 365 天 → reclaimable(namespace 释放)
9836
9949
  try {
9837
9950
  const r = reclaimRetiredAnchors(db);
@@ -10314,6 +10427,49 @@ try {
10314
10427
  db.exec('CREATE UNIQUE INDEX IF NOT EXISTS uniq_escrow_recipient_order_path ON pending_commission_escrow(recipient_user_id, order_id, attribution_path)');
10315
10428
  }
10316
10429
  catch { }
10430
+ // Codex #69 P1:pv_escrow_reserve(#1106 隔离负债账)回填历史 pending pv_pair escrow —— 按 delta 对账,不全量转。
10431
+ // 该列在 global_fund 上新增(line ~2319);#1106 之后新建的 pv_pair escrow 结算时【已】pv_escrow_reserve += wazAmount,
10432
+ // 但加列【之前】产生的 pending pv_pair 从没进过 reserve。升级窗口里两者混存。
10433
+ // ⚠️ 不能"全量 SUM(pending pv_pair) 再转":会把已隔离的新 escrow 二次扣 pool。
10434
+ // 正确做法:reserve 的目标值 = 当前所有 pending pv_pair 负债;只补差额 delta = liability - currentReserve 的正数部分。
10435
+ // · delta > 0:pool -= delta, reserve += delta(纯转账,total 不变);pool 不足则记 shortfall 风险项(基于 delta)。
10436
+ // · delta <= 0:已对齐/超额,不反向移动(避免误伤业务流);currentReserve > liability 记 anomaly 供核账。
10437
+ // 幂等(system_state 标志)。放在 pending_commission_escrow 建表/迁移之后(ALTER-after-CREATE 铁律)。
10438
+ try {
10439
+ db.exec("CREATE TABLE IF NOT EXISTS system_state (key TEXT PRIMARY KEY, value TEXT)");
10440
+ const done = db.prepare("SELECT value FROM system_state WHERE key = 'pv_escrow_reserve_backfilled'").get();
10441
+ if (!done) {
10442
+ db.transaction(() => {
10443
+ const liability = db.prepare(`SELECT COALESCE(SUM(amount),0) AS s FROM pending_commission_escrow WHERE status='pending' AND attribution_path='pv_pair'`).get().s;
10444
+ const gf = db.prepare("SELECT pool_balance, pv_escrow_reserve FROM global_fund WHERE id=1").get();
10445
+ const pool = gf?.pool_balance ?? 0;
10446
+ const currentReserve = gf?.pv_escrow_reserve ?? 0;
10447
+ const delta = Math.round((liability - currentReserve) * 100) / 100; // 只补"尚未隔离"的部分
10448
+ if (delta > 0) {
10449
+ db.prepare("UPDATE global_fund SET pv_escrow_reserve = pv_escrow_reserve + ?, pool_balance = pool_balance - ? WHERE id=1").run(delta, delta);
10450
+ console.log(`[migration pv_escrow_reserve backfill] 历史未隔离 pv_pair 负债 delta=${delta}(liability ${liability} - reserve ${currentReserve})从 pool 移入 reserve(pool ${pool}→${pool - delta})`);
10451
+ if (pool < delta) {
10452
+ const shortfall = Math.round((delta - pool) * 100) / 100;
10453
+ console.error(`[migration pv_escrow_reserve backfill] ⚠️ pool_balance(${pool}) < 待回填 delta(${delta});pool 已为负,缺口 ${shortfall} 需人工核账`);
10454
+ db.prepare("INSERT OR REPLACE INTO system_state (key, value) VALUES ('pv_escrow_reserve_backfill_shortfall', ?)").run(String(shortfall));
10455
+ }
10456
+ }
10457
+ else if (delta < 0) {
10458
+ // reserve 比 pending 负债还多 —— 不反向移动(避免误伤),只记异常供 admin 核账
10459
+ const anomaly = Math.round((currentReserve - liability) * 100) / 100;
10460
+ console.error(`[migration pv_escrow_reserve backfill] ⚠️ pv_escrow_reserve(${currentReserve}) > pending pv_pair 负债(${liability}),超额 ${anomaly};不反向移动,记 anomaly 供核账`);
10461
+ db.prepare("INSERT OR REPLACE INTO system_state (key, value) VALUES ('pv_escrow_reserve_backfill_anomaly', ?)").run(String(anomaly));
10462
+ }
10463
+ else {
10464
+ console.log(`[migration pv_escrow_reserve backfill] reserve(${currentReserve})已等于 pending pv_pair 负债(${liability}),无需回填`);
10465
+ }
10466
+ db.prepare("INSERT OR REPLACE INTO system_state (key, value) VALUES ('pv_escrow_reserve_backfilled', '1')").run();
10467
+ })();
10468
+ }
10469
+ }
10470
+ catch (e) {
10471
+ console.error('[migration pv_escrow_reserve backfill]', e);
10472
+ }
10317
10473
  // 6. INSERT 5 个 RFC-002 protocol_params(独立 INSERT,不动 DEFAULT_PARAMS array)
10318
10474
  // 两个标 requires_meta_rule_change=1:require_passkey + consent_delay_seconds(P0-4 闭环)
10319
10475
  const RFC002_PARAMS = [
package/dist/version.js CHANGED
@@ -29,4 +29,4 @@ export const SOFTWARE_VERSION = pkg.version;
29
29
  * (capability matrix §② / entity dictionary §①); the CONTRACT_CHANGES `kind` classifies whether
30
30
  * it is breaking. Additive changes (kind:'added') are safe for agents to ignore. Guarded by
31
31
  * tests/test-contract-fingerprint.ts + docs/CONTRACT-LOCK.json. */
32
- export const CONTRACT_VERSION = 2;
32
+ export const CONTRACT_VERSION = 4;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seasonkoh/webaz",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "[PRE-LAUNCH] Agent-native decentralized commerce protocol. Humans and AI agents trade on the same protocol via MCP tools. ⚠️ Repository currently private until W8 public launch — GitHub links may return 404. See https://webaz.xyz for status.",
5
5
  "main": "dist/mcp.js",
6
6
  "bin": {
@@ -20,10 +20,53 @@
20
20
  "enforcement": "tsx src/cron-enforcement.ts",
21
21
  "pwa": "tsx src/pwa/server.ts",
22
22
  "schema:verify": "tsx scripts/schema-verify.ts",
23
+ "routes:seam-check": "tsx scripts/routes-seam-guard.ts",
24
+ "pg:schema": "tsx scripts/gen-pg-schema.ts",
25
+ "pg:verify": "tsx scripts/pg-schema-verify.ts",
26
+ "test:pg-placeholders": "tsx scripts/test-pg-placeholders.ts",
27
+ "test:pg-datetime": "tsx scripts/test-pg-datetime.ts",
28
+ "test:agent-task-spec": "tsx scripts/test-agent-task-spec.ts",
23
29
  "contract:verify": "tsx tests/test-contract-fingerprint.ts",
24
30
  "license:check": "tsx scripts/license-invariant-check.ts",
25
31
  "meta-rules:check": "tsx scripts/meta-rules-invariant-check.ts",
26
- "params:check": "tsx scripts/meta-rule-locked-params-check.ts"
32
+ "params:check": "tsx scripts/meta-rule-locked-params-check.ts",
33
+ "preflight": "tsx scripts/preflight.ts",
34
+ "test:github-credential": "tsx scripts/test-github-credential.ts",
35
+ "test:github-fetch-adapter": "tsx scripts/test-github-fetch-adapter.ts",
36
+ "test:github-authenticated-read": "tsx scripts/test-github-authenticated-read.ts",
37
+ "test:github-credential-store": "tsx scripts/test-github-credential-store-schema.ts",
38
+ "test:github-credential-ingestion": "tsx scripts/test-github-credential-ingestion.ts",
39
+ "test:identity-binding": "tsx scripts/test-identity-binding.ts",
40
+ "iron-rules:identity-claim": "tsx scripts/identity-claim-iron-rules-guard.ts",
41
+ "test:iron-rules-guard": "tsx scripts/test-identity-claim-iron-rules-guard.ts",
42
+ "test:human-presence": "tsx tests/test-human-presence.ts",
43
+ "test:identity-claim-challenge": "tsx scripts/test-identity-claim-challenge-schema.ts",
44
+ "test:identity-claim-engine": "tsx scripts/test-identity-claim-engine.ts",
45
+ "test:identity-claim-proof-verifier": "tsx scripts/test-identity-claim-proof-verifier.ts",
46
+ "test:identity-claim-challenge-engine": "tsx scripts/test-identity-claim-challenge-engine.ts",
47
+ "test:identity-claim-api": "tsx scripts/test-identity-claim-api.ts",
48
+ "test:identity-claim-read": "tsx scripts/test-identity-claim-read.ts",
49
+ "test:build-reputation-boundary": "tsx scripts/test-build-reputation-boundary.ts",
50
+ "test:contribution-score-contract": "tsx scripts/test-contribution-score-contract.ts",
51
+ "test:contribution-score-evidence": "tsx scripts/test-contribution-score-evidence.ts",
52
+ "test:contribution-score-read": "tsx scripts/test-contribution-score-read.ts",
53
+ "test:contributor-entry-relationship-contract": "tsx scripts/test-contributor-entry-relationship-contract.ts",
54
+ "test-public-contributor-entry-contract": "tsx scripts/test-public-contributor-entry-contract.ts",
55
+ "test-private-dogfood-runbook-contract": "tsx scripts/test-private-dogfood-runbook-contract.ts",
56
+ "test-private-dogfood-case-pack-contract": "tsx scripts/test-private-dogfood-case-pack-contract.ts",
57
+ "test-future-task-board-design-contract": "tsx scripts/test-future-task-board-design-contract.ts",
58
+ "test:build-task-agent-metadata": "tsx scripts/test-build-task-agent-metadata.ts",
59
+ "test-dogfood-dryrun-gap-report-contract": "tsx scripts/test-dogfood-dryrun-gap-report-contract.ts",
60
+ "test:task-board-read": "tsx scripts/test-task-board-read.ts",
61
+ "test:task-board-participation": "tsx scripts/test-task-board-participation.ts",
62
+ "test:task-proposals": "tsx scripts/test-task-proposals.ts",
63
+ "test:public-contribution-pages": "tsx scripts/test-public-contribution-pages.ts",
64
+ "test:mcp-contribute": "tsx scripts/test-mcp-contribute.ts",
65
+ "test:proposal-admin-ui": "tsx scripts/test-proposal-admin-ui.ts",
66
+ "check:pwa-syntax": "node --check src/pwa/public/app.js && node --check src/pwa/public/i18n.js && node --check src/pwa/public/sw.js",
67
+ "test:rewards-vs-contribution-copy": "tsx scripts/test-rewards-vs-contribution-copy.ts",
68
+ "test:invite-code-narrowing": "tsx scripts/test-invite-code-narrowing.ts",
69
+ "test:shop-referral-attribution": "tsx scripts/test-shop-referral-attribution.ts"
27
70
  },
28
71
  "keywords": [
29
72
  "mcp",
@@ -55,6 +98,7 @@
55
98
  "@types/qrcode": "^1.5.6",
56
99
  "better-sqlite3": "^12.9.0",
57
100
  "express": "^5.2.1",
101
+ "pg": "^8.21.0",
58
102
  "qrcode": "^1.5.4",
59
103
  "undici": "^8.3.0",
60
104
  "viem": "^2.48.11",
@@ -64,6 +108,7 @@
64
108
  "@types/better-sqlite3": "^7.6.13",
65
109
  "@types/express": "^5.0.6",
66
110
  "@types/node": "^25.6.2",
111
+ "@types/pg": "^8.20.0",
67
112
  "tsx": "^4.21.0",
68
113
  "typescript": "^6.0.3"
69
114
  },