@seasonkoh/webaz 0.1.18 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/layer0-foundation/L0-2-state-machine/engine.js +30 -54
- package/dist/layer1-agent/L1-1-mcp-server/server.js +15 -2
- package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +31 -2
- package/dist/layer2-business/L2-9-contribution/build-reputation-engine.js +4 -0
- package/dist/pwa/routes/build-feedback.js +3 -2
- package/dist/pwa/routes/orders-create.js +7 -2
- package/dist/pwa/server.js +39 -4
- package/package.json +1 -1
|
@@ -199,13 +199,28 @@ function settleFault(db, orderId, faultState) {
|
|
|
199
199
|
const buyerId = order.buyer_id;
|
|
200
200
|
const sellerId = order.seller_id;
|
|
201
201
|
const isSecondhand = order.source === 'secondhand';
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
|
|
202
|
+
// RFC-008 stage 1(印钱 bug 修复):违约没收【只按订单的 stake_backing 快照】,绝不假设已锁、绝不超背书。
|
|
203
|
+
// 旧 bug:无条件 staked -= 0.15×total,但生产下单根本没锁 stake → staked 转负 + 印钱。
|
|
204
|
+
// 现在:没收 = min(penalty, stake_backing),从 staked 扣,封顶背书额 → 永不转负、永不印钱。
|
|
205
|
+
// 起步免赔付(require_seller_stake=0 → backing=0):没收 0,买家已全额退款,卖家仅掉信誉。
|
|
206
|
+
// stage 2(收紧后):penalty 解耦为 fault_penalty_rate×total + 不足部分扣自由 balance(仅背书订单)。
|
|
207
|
+
const sellerStakeRate = 0.15; // stage 1 penalty 基数(stage 2 改 fault_penalty_rate=0.30 并解耦)
|
|
206
208
|
const stakeAmount = isSecondhand ? 0 : Math.round(total * sellerStakeRate * 100) / 100;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
+
const orderStakeBacking = Math.max(0, Math.round(Number(order.stake_backing || 0) * 100) / 100);
|
|
210
|
+
// 没收并按 buyer 50% / sys_protocol 50% 分配,返回实扣额(= 0 表示起步免赔付,无没收无印钱)
|
|
211
|
+
const forfeitBackedStake = (penaltyBase) => {
|
|
212
|
+
const actualDeduct = Math.min(penaltyBase, orderStakeBacking); // 封顶背书额 → 绝不超已锁、绝不转负
|
|
213
|
+
if (actualDeduct <= 0)
|
|
214
|
+
return 0;
|
|
215
|
+
db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(actualDeduct, sellerId);
|
|
216
|
+
const compensation = Math.round(actualDeduct * 0.5 * 100) / 100;
|
|
217
|
+
const protocolShare = Math.round((actualDeduct - compensation) * 100) / 100;
|
|
218
|
+
if (compensation > 0)
|
|
219
|
+
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compensation, buyerId);
|
|
220
|
+
if (protocolShare > 0)
|
|
221
|
+
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(protocolShare, sysUserId);
|
|
222
|
+
return actualDeduct;
|
|
223
|
+
};
|
|
209
224
|
// P0.1:RFQ 路径的 bid_stake_held — fault 时由各分支按规则处理
|
|
210
225
|
const bidStakeHeld = Number(order.bid_stake_held || 0);
|
|
211
226
|
if (faultState === 'fault_seller') {
|
|
@@ -222,31 +237,9 @@ function settleFault(db, orderId, faultState) {
|
|
|
222
237
|
if (compToSys > 0)
|
|
223
238
|
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compToSys, sysUserId);
|
|
224
239
|
}
|
|
225
|
-
// 2.
|
|
226
|
-
if (stakeAmount > 0)
|
|
227
|
-
|
|
228
|
-
let actualDeduct = 0;
|
|
229
|
-
if (stakeLocked) {
|
|
230
|
-
// 已锁 → stake 全额扣(必然可扣,因 stake 已 lock 不可挪用)
|
|
231
|
-
db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(stakeAmount, sellerId);
|
|
232
|
-
actualDeduct = stakeAmount;
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
// 未锁 → 从 seller.balance 扣(不足则只扣到 0,差额不发放,不印钱)
|
|
236
|
-
const w = db.prepare('SELECT balance FROM wallets WHERE user_id = ?').get(sellerId);
|
|
237
|
-
actualDeduct = Math.min(stakeAmount, Number(w?.balance ?? 0));
|
|
238
|
-
if (actualDeduct > 0) {
|
|
239
|
-
db.prepare('UPDATE wallets SET balance = balance - ? WHERE user_id = ?').run(actualDeduct, sellerId);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
// 按 actualDeduct 比例分配(buyer 50% + sys_protocol 50%),保证 入 = 出
|
|
243
|
-
const compensation = Math.round(actualDeduct * 0.5 * 100) / 100;
|
|
244
|
-
const protocolShare = Math.round((actualDeduct - compensation) * 100) / 100;
|
|
245
|
-
if (compensation > 0)
|
|
246
|
-
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compensation, buyerId);
|
|
247
|
-
if (protocolShare > 0)
|
|
248
|
-
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(protocolShare, sysUserId);
|
|
249
|
-
}
|
|
240
|
+
// 2. 没收(封顶 stake_backing,绝不印钱)→ buyer 50% / sys_protocol 50%
|
|
241
|
+
if (stakeAmount > 0)
|
|
242
|
+
forfeitBackedStake(stakeAmount);
|
|
250
243
|
// 3. 库存回退(非二手)
|
|
251
244
|
if (!isSecondhand)
|
|
252
245
|
db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
|
|
@@ -278,27 +271,9 @@ function settleFault(db, orderId, faultState) {
|
|
|
278
271
|
if (compToSys > 0)
|
|
279
272
|
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compToSys, sysUserId);
|
|
280
273
|
}
|
|
281
|
-
// 3.
|
|
282
|
-
if (stakeAmount > 0)
|
|
283
|
-
|
|
284
|
-
if (stakeLocked) {
|
|
285
|
-
db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(stakeAmount, sellerId);
|
|
286
|
-
actualDeduct = stakeAmount;
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
const w = db.prepare('SELECT balance FROM wallets WHERE user_id = ?').get(sellerId);
|
|
290
|
-
actualDeduct = Math.min(stakeAmount, Number(w?.balance ?? 0));
|
|
291
|
-
if (actualDeduct > 0) {
|
|
292
|
-
db.prepare('UPDATE wallets SET balance = balance - ? WHERE user_id = ?').run(actualDeduct, sellerId);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
const compensation = Math.round(actualDeduct * 0.5 * 100) / 100;
|
|
296
|
-
const protocolShare = Math.round((actualDeduct - compensation) * 100) / 100;
|
|
297
|
-
if (compensation > 0)
|
|
298
|
-
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compensation, buyerId);
|
|
299
|
-
if (protocolShare > 0)
|
|
300
|
-
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(protocolShare, sysUserId);
|
|
301
|
-
}
|
|
274
|
+
// 3. 没收(self-fulfill seller 违约;封顶 stake_backing,绝不印钱)→ buyer 50% / sys_protocol 50%
|
|
275
|
+
if (stakeAmount > 0)
|
|
276
|
+
forfeitBackedStake(stakeAmount);
|
|
302
277
|
// 4. 库存回退
|
|
303
278
|
if (!isSecondhand)
|
|
304
279
|
db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
|
|
@@ -318,9 +293,10 @@ function settleFault(db, orderId, faultState) {
|
|
|
318
293
|
if (bidStakeHeld > 0) {
|
|
319
294
|
db.prepare('UPDATE wallets SET balance = balance + ?, staked = staked - ? WHERE user_id = ?').run(bidStakeHeld, bidStakeHeld, sellerId);
|
|
320
295
|
}
|
|
321
|
-
|
|
296
|
+
// seller 无责 → 退还其【该单实际背书的 stake】(= stake_backing;起步阶段=0,无可退)
|
|
297
|
+
if (orderStakeBacking > 0) {
|
|
322
298
|
db.prepare('UPDATE wallets SET staked = staked - ?, balance = balance + ? WHERE user_id = ?')
|
|
323
|
-
.run(
|
|
299
|
+
.run(orderStakeBacking, orderStakeBacking, sellerId);
|
|
324
300
|
}
|
|
325
301
|
// 3. 库存回退
|
|
326
302
|
if (!isSecondhand)
|
|
@@ -1359,7 +1359,7 @@ Gate by type: ux_issue/bug (reporting = using) → login only, NO Passkey, anyon
|
|
|
1359
1359
|
|
|
1360
1360
|
Actions:
|
|
1361
1361
|
- list_open (default): open tasks (opt. area filter)
|
|
1362
|
-
- claim: take an open task; provenance=human|ai_assisted|ai_authored (self-declared, not detected); auto-expires ~7d if not submitted
|
|
1362
|
+
- claim: take an open task; provenance=human|ai_assisted|ai_authored (self-declared, not detected); auto-expires ~7d if not submitted. Returns a **handoff** (repo + AGENTS.md + PR flow) — point a coding agent at it to actually do the work; the human needn't know git.
|
|
1363
1363
|
- submit: mark in_review with pr_ref
|
|
1364
1364
|
- status: tasks you hold (claimed/in_review)
|
|
1365
1365
|
- profile: your build dashboard — KPI/tier/restrictions+appeal, self-view (private, no public leaderboard)
|
|
@@ -1439,9 +1439,22 @@ async function handleContribute(args) {
|
|
|
1439
1439
|
const tid = args.task_id;
|
|
1440
1440
|
if (!tid)
|
|
1441
1441
|
return { error: 'task_id required for action=claim' };
|
|
1442
|
-
|
|
1442
|
+
const r = await apiCall('/api/build-tasks/' + encodeURIComponent(tid) + '/claim', {
|
|
1443
1443
|
method: 'POST', apiKey, body: { provenance: args.provenance },
|
|
1444
1444
|
});
|
|
1445
|
+
// RFC-006 断点1(b)交接:认领成功后直接下发"怎么真正动手",让贡献者的【编码 agent】接手 git/PR。
|
|
1446
|
+
// 关键:人不必会 git——人的编码 agent(如 Claude Code)做;人(Passkey 真人)担责。
|
|
1447
|
+
if (!r.error) {
|
|
1448
|
+
r.handoff = {
|
|
1449
|
+
repo: 'https://github.com/seasonsagents-art/webaz',
|
|
1450
|
+
start_here: 'Read AGENTS.md (project map + before-you-code + PR flow), then CONTRIBUTING.md.',
|
|
1451
|
+
do_the_work: 'Point a coding agent (e.g. Claude Code) at the repo; work on a single-topic branch. The buyer/shopping agent is not the coding agent — hand off to one.',
|
|
1452
|
+
pr_flow: 'Commit with DCO sign-off (git commit -s). If AI-authored, add 🤖🤖🤖 to the PR title + a meta-rule trace. Humans merge — no auto-merge.',
|
|
1453
|
+
then: `When the PR is open, report it back: webaz_contribute action=submit task_id=${tid} pr_ref=#<N>.`,
|
|
1454
|
+
human_note: "You don't need to know git — your coding agent does it; you (the Passkey-bound human) stay accountable.",
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
return r;
|
|
1445
1458
|
}
|
|
1446
1459
|
if (action === 'submit') {
|
|
1447
1460
|
const tid = args.task_id;
|
|
@@ -2,6 +2,8 @@ import { generateId } from '../../layer0-foundation/L0-1-database/schema.js';
|
|
|
2
2
|
// RFC-006 不变量 1:建设贡献记入【独立】build_reputation 池,不再写交易 reputation_scores
|
|
3
3
|
//(旧:recordRepEvent('feedback_accepted') 会污染 verifier/arbitrator 准入,已隔离)。
|
|
4
4
|
import { creditBuildReputation, BUILD_POINTS } from '../L2-9-contribution/build-reputation-engine.js';
|
|
5
|
+
// RFC-006 桥(use→build 漏斗补全):采纳的 proposal → 自动建 build_task + 邀请提案人来认领。
|
|
6
|
+
import { createBuildTask } from '../L2-9-contribution/build-tasks-engine.js';
|
|
5
7
|
export const FB_TYPES = new Set(['ux_issue', 'bug', 'proposal']);
|
|
6
8
|
export const FB_SEVERITY = new Set(['low', 'annoying', 'blocking']);
|
|
7
9
|
export const FB_STATUS = new Set(['received', 'triaged', 'in_progress', 'resolved', 'declined', 'duplicate']);
|
|
@@ -38,6 +40,8 @@ export function initBuildFeedbackSchema(db) {
|
|
|
38
40
|
'ALTER TABLE build_feedback ADD COLUMN ai_summary TEXT', // 一句话摘要(给 maintainer 扫)
|
|
39
41
|
'ALTER TABLE build_feedback ADD COLUMN ai_models TEXT', // 参与的模型 + 是否一致
|
|
40
42
|
'ALTER TABLE build_feedback ADD COLUMN ai_triaged_at TEXT',
|
|
43
|
+
// RFC-006 桥:采纳的 proposal 被 promote 成 build_task 时,记其 task id(use→build 漏斗:反馈→协调)
|
|
44
|
+
'ALTER TABLE build_feedback ADD COLUMN promoted_task_id TEXT',
|
|
41
45
|
]) {
|
|
42
46
|
try {
|
|
43
47
|
db.exec(stmt);
|
|
@@ -133,7 +137,7 @@ function parse(row) {
|
|
|
133
137
|
return { ...rest, scene };
|
|
134
138
|
}
|
|
135
139
|
export function listMyBuildFeedback(db, userId) {
|
|
136
|
-
const rows = db.prepare(`SELECT id, type, area, severity, subject, body, status, dedup_of, resolution, credited_points, created_at, updated_at
|
|
140
|
+
const rows = db.prepare(`SELECT id, type, area, severity, subject, body, status, dedup_of, resolution, credited_points, promoted_task_id, created_at, updated_at
|
|
137
141
|
FROM build_feedback WHERE user_id = ? ORDER BY created_at DESC LIMIT 100`).all(userId);
|
|
138
142
|
return rows;
|
|
139
143
|
}
|
|
@@ -179,7 +183,32 @@ export function adminUpdateBuildFeedback(db, u) {
|
|
|
179
183
|
WHERE id = ?`).run(newStatus, u.resolution ?? null, u.rfcDraft ?? null, credited, u.adminId, u.id);
|
|
180
184
|
if (newStatus !== fromStatus)
|
|
181
185
|
logEvent(db, u.id, u.adminId, fromStatus, newStatus, u.resolution ?? null);
|
|
182
|
-
|
|
186
|
+
// RFC-006 桥(use→build 漏斗补全):maintainer 采纳提案时可 promote 成可认领的 build_task,
|
|
187
|
+
// 并【邀请提案人】来认领——把"反馈被采纳"接到"来一起建设",补上漏斗最大断点。
|
|
188
|
+
// 幂等:已 promote 过(promoted_task_id 非空)不重复建。
|
|
189
|
+
let promoted_task_id;
|
|
190
|
+
const already = row.promoted_task_id || null;
|
|
191
|
+
if (u.promoteToTask && !already && row.type === 'proposal') {
|
|
192
|
+
const title = (row.subject || row.body || 'contributor proposal').slice(0, 200);
|
|
193
|
+
const created = createBuildTask(db, {
|
|
194
|
+
creatorId: u.adminId,
|
|
195
|
+
title,
|
|
196
|
+
area: row.area || undefined,
|
|
197
|
+
description: `From accepted proposal ${u.id} (by ${row.user_id}).\n\n${row.body || ''}`.slice(0, 4000),
|
|
198
|
+
rfcRef: row.rfc_draft || undefined,
|
|
199
|
+
});
|
|
200
|
+
if ('id' in created) {
|
|
201
|
+
promoted_task_id = created.id;
|
|
202
|
+
db.prepare('UPDATE build_feedback SET promoted_task_id = ? WHERE id = ?').run(promoted_task_id, u.id);
|
|
203
|
+
// 邀请提案人:通知 + 反馈闭环里会显示 promoted_task_id
|
|
204
|
+
try {
|
|
205
|
+
db.prepare(`INSERT INTO notifications (id, user_id, type, title, body) VALUES (?,?,?,?,?)`).run(generateId('ntf'), row.user_id, 'build_invite', '你的提案被采纳了 — 来一起建设?', `提案「${title}」已被采纳并建成可认领任务 ${promoted_task_id}。在「我的共建」用 webaz_contribute 认领即可参与实现。`);
|
|
206
|
+
}
|
|
207
|
+
catch { /* notifications 可选,不阻断 */ }
|
|
208
|
+
logEvent(db, u.id, u.adminId, newStatus, newStatus, `promoted → task ${promoted_task_id}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return { ok: true, credited, ...(credit_skipped_no_anchor ? { credit_skipped_no_anchor: true } : {}), ...(promoted_task_id ? { promoted_task_id } : {}) };
|
|
183
212
|
}
|
|
184
213
|
// ─── RFC-005 Phase 2:AI triage(advisory)─────────────────────────
|
|
185
214
|
// 给"内部反馈"打标,不碰代码、不 resolve、不记功(那是人类的)。无 key 也能跑(只做确定性去重 + 置 triaged)。
|
|
@@ -57,6 +57,10 @@ export function creditBuildReputation(db, userId, source, points, refId, note) {
|
|
|
57
57
|
}
|
|
58
58
|
return { credited: points };
|
|
59
59
|
}
|
|
60
|
+
// RFC-006 stage 4(恶意管理)= 复用现有问责中间件,**无需新代码**:
|
|
61
|
+
// build_tasks 是 api_key 写端点,被 strike 至 suspend_7d/permanent 的贡献者已被 isApiKeyBlocked
|
|
62
|
+
// (server.ts)挡在所有写之外,包括建设。strike/blocklist/outlier 对建设贡献自动生效。
|
|
63
|
+
// 看板这里只【展示】当事人的活跃 strike + 申诉入口(透明先于强制);真人申诉走现成 strikes/:id/appeal。
|
|
60
64
|
// 贡献者【自查】档案 —— KPI + 等级 + 来源拆分 + provenance + 限制/惩罚 + 申诉入口。
|
|
61
65
|
// 不变量 3:仅本人可调(路由层 auth);不做公开榜。
|
|
62
66
|
export function getBuildProfile(db, userId) {
|
|
@@ -71,9 +71,10 @@ export function registerBuildFeedbackRoutes(app, deps) {
|
|
|
71
71
|
const admin = requireSupportAdmin(req, res);
|
|
72
72
|
if (!admin)
|
|
73
73
|
return;
|
|
74
|
-
const { status, resolution, rfc_draft, credit } = req.body ?? {};
|
|
74
|
+
const { status, resolution, rfc_draft, credit, promote_to_task } = req.body ?? {};
|
|
75
75
|
const result = adminUpdateBuildFeedback(db, {
|
|
76
|
-
id: String(req.params.id), status, resolution, rfcDraft: rfc_draft, credit: !!credit,
|
|
76
|
+
id: String(req.params.id), status, resolution, rfcDraft: rfc_draft, credit: !!credit,
|
|
77
|
+
promoteToTask: !!promote_to_task, adminId: admin.id,
|
|
77
78
|
});
|
|
78
79
|
if ('error' in result)
|
|
79
80
|
return void res.status(404).json(result);
|
|
@@ -223,6 +223,11 @@ export function registerOrdersCreateRoutes(app, deps) {
|
|
|
223
223
|
const buyerRegionSnapshot = buyer?.region || 'global';
|
|
224
224
|
// P2P:若为 P2P 商品,下单时快照 content_hash(争议时凭买家所见 hash 判定)
|
|
225
225
|
const contentHashSnapshot = (Number(product.p2p_mode) === 1 && product.content_hash) ? String(product.content_hash) : null;
|
|
226
|
+
// RFC-008 stage 1:赔付背书快照。起步免赔付阶段(require_seller_stake=0)= 0 → 违约只退款不没收、不印钱、零门槛。
|
|
227
|
+
// ⚠️ "要求质押"档(=1,下单锁 stake、backing=total×stake_rate(信誉))属后续【收紧】阶段,届时在此计算并锁定。
|
|
228
|
+
const stakeBacking = Number(getProtocolParam('require_seller_stake', 0)) === 1
|
|
229
|
+
? 0 // TODO(tighten stage): backing = total × stakeRate(seller reputation) + 在此 lock balance→staked
|
|
230
|
+
: 0;
|
|
226
231
|
db.prepare(`INSERT INTO orders (
|
|
227
232
|
id, product_id, buyer_id, seller_id, quantity, unit_price, total_amount, escrow_amount,
|
|
228
233
|
status, shipping_address, notes, pay_deadline, accept_deadline, ship_deadline,
|
|
@@ -230,8 +235,8 @@ export function registerOrdersCreateRoutes(app, deps) {
|
|
|
230
235
|
l1_uid, l2_uid, l3_uid, snapshot_commission_rate, buyer_region, content_hash_at_order,
|
|
231
236
|
delivery_window, variant_id, variant_options_snapshot,
|
|
232
237
|
gift_recipient_name, gift_recipient_phone, gift_message, insurance_premium,
|
|
233
|
-
anonymous_recipient, recipient_code, donation_amount
|
|
234
|
-
) VALUES (?,?,?,?,?,?,?,?,'created'
|
|
238
|
+
anonymous_recipient, recipient_code, donation_amount, stake_backing
|
|
239
|
+
) VALUES (?,?,?,?,?,?,?,?,'created',?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(orderId, product.id, user.id, product.seller_uid, reqQty, basePrice, totalAmount, totalAmount, shipping_address, notes || null, addHours(now, 24), addHours(now, 48), addHours(now, 120), addHours(now, 168), addHours(now, 336), addHours(now, 408), l1, l2, l3, snapshotRate, buyerRegionSnapshot, contentHashSnapshot, deliveryWindowJson, variant ? variant.id : null, variant ? variant.options_json : null, giftRecipientName, giftRecipientPhone, giftMessage, insurancePremium, anonymousFlag, recipientCode, donationAmount, stakeBacking);
|
|
235
240
|
// 协议层:写 genesis 事件 — order 创建(必然是 buyer 自己)
|
|
236
241
|
try {
|
|
237
242
|
appendOrderEvent(db, {
|
package/dist/pwa/server.js
CHANGED
|
@@ -531,6 +531,9 @@ for (const stmt of [
|
|
|
531
531
|
'ALTER TABLE orders ADD COLUMN gift_message TEXT',
|
|
532
532
|
// C-3: 订单保险 — 已支付保费(默认 1%,争议时若卖家余额不足由保险池补足)
|
|
533
533
|
'ALTER TABLE orders ADD COLUMN insurance_premium REAL DEFAULT 0',
|
|
534
|
+
// RFC-008 stage 1:每单【赔付背书】快照 = 该单实际背书的卖家质押额。
|
|
535
|
+
// 起步免赔付阶段(require_seller_stake=0)= 0;违约结算只按此数没收,绝不扣未背书的钱 → 根治印钱 bug。
|
|
536
|
+
'ALTER TABLE orders ADD COLUMN stake_backing REAL DEFAULT 0',
|
|
534
537
|
]) {
|
|
535
538
|
try {
|
|
536
539
|
db.exec(stmt);
|
|
@@ -767,10 +770,14 @@ try {
|
|
|
767
770
|
catch { }
|
|
768
771
|
// 已注册默认参数(首次启动 seed) — P0-2 加 min/max 边界
|
|
769
772
|
const DEFAULT_PARAMS = [
|
|
770
|
-
|
|
771
|
-
{ key: '
|
|
773
|
+
// RFC-008:平台费硬帽 2%(=当前稳态 → 治理只能在 0–2% 减免、永不涨)。合计封顶 = 平台费2% + fund_base1% = 3%。宪法级合法性见 CHARTER 修订(单独治理步)。
|
|
774
|
+
{ key: 'protocol_fee_rate_shop', value: '0.02', type: 'number', description: '商家订单平台费率(RFC-008 硬帽 2%,只减不涨;前期可减免)', category: 'fee', min: 0, max: 0.02 },
|
|
775
|
+
{ key: 'protocol_fee_rate_secondhand', value: '0.01', type: 'number', description: '二手订单平台费率(RFC-008 硬帽 2%,只减不涨)', category: 'fee', min: 0, max: 0.02 },
|
|
772
776
|
{ key: 'default_commission_rate', value: '0.05', type: 'number', description: '新商品默认分享佣金(对齐小红书 5-10%)', category: 'fee', min: 0, max: 0.50 },
|
|
773
|
-
|
|
777
|
+
// RFC-008:fund_base 硬帽 1%;pre-launch 减免到 0(社区基金按真实 GMV 注入,0 GMV 时是无回报的税)。有真实 GMV 再由治理开启(≤1%)。
|
|
778
|
+
{ key: 'fund_base_rate', value: '0', type: 'number', description: '协议基金池基础费率(RFC-008 硬帽 1%;pre-launch 减免=0,有真实 GMV 再由治理开启 ≤1%)', category: 'fee', min: 0, max: 0.01 },
|
|
779
|
+
// RFC-008:起步免赔付门槛。0 = bootstrap(新商家零质押、违约免赔付只退款+掉信誉,降进入门槛);1 = 要求卖家质押(下单锁 stake、违约真没收)。上轨道后由治理开启。
|
|
780
|
+
{ key: 'require_seller_stake', value: '0', type: 'number', description: 'RFC-008 是否要求卖家质押(0=起步免赔付/零门槛,1=要求质押/真没收)', category: 'fee', min: 0, max: 1 },
|
|
774
781
|
{ key: 'checkin_base_reward', value: '0.5', type: 'number', description: '每日签到基础奖励 WAZ', category: 'reward', min: 0, max: 10 },
|
|
775
782
|
{ key: 'streak_bonus_7', value: '5', type: 'number', description: '7 天里程碑额外奖励', category: 'reward', min: 0, max: 100 },
|
|
776
783
|
{ key: 'streak_bonus_30', value: '20', type: 'number', description: '30 天里程碑额外奖励', category: 'reward', min: 0, max: 500 },
|
|
@@ -901,6 +908,31 @@ try {
|
|
|
901
908
|
catch (e) {
|
|
902
909
|
console.error('[migration #1006]', e);
|
|
903
910
|
}
|
|
911
|
+
// RFC-008 迁移:费帽收紧 + fund_base pre-launch 减免。bounds 是协议护栏 → 无条件强制收窄(幂等)。
|
|
912
|
+
// 平台费帽 → 2%(=稳态,只减不涨);fund_base 帽 → 1%;合计封顶 3%。fund_base 值减免到 0(仅原始 0.01、未被治理改过)。
|
|
913
|
+
try {
|
|
914
|
+
const feeCap = db.prepare(`UPDATE protocol_params SET max_value = 0.02, updated_at = datetime('now')
|
|
915
|
+
WHERE key IN ('protocol_fee_rate_shop','protocol_fee_rate_secondhand') AND max_value > 0.02`).run();
|
|
916
|
+
if (feeCap.changes > 0)
|
|
917
|
+
console.log(`[migration RFC-008] 平台费硬帽收紧 ${feeCap.changes} 项 → max 2%`);
|
|
918
|
+
const fbCap = db.prepare(`UPDATE protocol_params SET max_value = 0.01, updated_at = datetime('now')
|
|
919
|
+
WHERE key = 'fund_base_rate' AND max_value > 0.01`).run();
|
|
920
|
+
if (fbCap.changes > 0)
|
|
921
|
+
console.log(`[migration RFC-008] fund_base 硬帽收紧 → max 1%`);
|
|
922
|
+
const fb = db.prepare(`UPDATE protocol_params SET value = '0', default_value = '0', updated_at = datetime('now')
|
|
923
|
+
WHERE key = 'fund_base_rate' AND value = '0.01' AND updated_by IS NULL`).run();
|
|
924
|
+
if (fb.changes > 0) {
|
|
925
|
+
console.log(`[migration RFC-008] fund_base_rate 0.01 → 0 (pre-launch 减免)`);
|
|
926
|
+
try {
|
|
927
|
+
db.prepare(`INSERT INTO protocol_params_log (id, key, old_value, new_value, changed_by, action)
|
|
928
|
+
VALUES (?,?,?,?,?,'migrate')`).run(generateId('ppl'), 'fund_base_rate', '0.01', '0', 'migration_RFC-008');
|
|
929
|
+
}
|
|
930
|
+
catch { }
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
catch (e) {
|
|
934
|
+
console.error('[migration RFC-008]', e);
|
|
935
|
+
}
|
|
904
936
|
// Wave G-2: USDC ↔ WAZ 转换助手
|
|
905
937
|
function usdcToWaz(usdc) {
|
|
906
938
|
const rate = getProtocolParam('waz_usdc_rate', 1.0);
|
|
@@ -8946,8 +8978,11 @@ function settleOrder(orderId) {
|
|
|
8946
8978
|
// 用单语句 UPDATE ... WHERE stake_locked_at IS NULL 原子拿"是否首次"判定,
|
|
8947
8979
|
// 避免读后写竞态(旧实现两条语句,迁 PG / 多 worker 时可能双锁)
|
|
8948
8980
|
// M8: 二手商品无 stake 概念,跳过
|
|
8981
|
+
// RFC-008 stage 1:起步免赔付阶段(require_seller_stake=0)不锁 stake —— 与 settleFault 按 stake_backing(=0)结算一致,
|
|
8982
|
+
// 消除"成功时锁 product.stake_amount 但违约按 backing=0 不没收"的旧三口径不一致。收紧档(=1)由后续阶段统一在下单锁。
|
|
8983
|
+
const requireSellerStake = Number(getProtocolParam('require_seller_stake', 0)) === 1;
|
|
8949
8984
|
let stakeToLock = 0;
|
|
8950
|
-
if (!isSecondhand) {
|
|
8985
|
+
if (!isSecondhand && requireSellerStake) {
|
|
8951
8986
|
const lockResult = db.prepare(`UPDATE products SET stake_locked_at = datetime('now') WHERE id = ? AND stake_locked_at IS NULL`).run(order.product_id);
|
|
8952
8987
|
if (lockResult.changes === 1) {
|
|
8953
8988
|
const sellerTrust = db.prepare(`SELECT level FROM reputation_scores WHERE user_id = ?`).get(order.seller_id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seasonkoh/webaz",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
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": {
|