feat: implement S5 冰朔直通部署规则 (direct-deploy bypass)

S5 Rule: When instructions come from 冰朔 (TCS-0002) or are signed by
trusted signers (AG-SY-01/TCS-0002/ICE-0002) with ZY- prefix,
deployment goes directly to production bypassing preview/SkyEye/approval.

- Add isDirectDeploySource() in autonomy-engine.js (single source of truth)
- Add direct-deploy bypass in approval.js POST /request endpoint
- Add directDeployRule config in autonomy-rules.json
- Add deploy audit log (deploy-YYYY-MM-DD.jsonl) for traceability
- Developer (Level 0-2) changes still follow full S2 approval flow
- Fix watchdog for-of destructuring to match codebase var style

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/6ee737eb-684b-44b8-aeaa-f73eb7c6f9c2
This commit is contained in:
copilot-swe-agent[bot] 2026-03-25 08:54:40 +00:00
parent 721974ca76
commit 6fab64b1b9
4 changed files with 143 additions and 5 deletions

View File

@ -43,6 +43,21 @@
]
},
"deploymentFlow": {
"directDeployRule": {
"id": "S5",
"description": "冰朔直通部署规则:冰朔签发或口头下达的所有指令,执行完毕后直接部署到正式站",
"conditions": [
"TCS-0002 发起的请求",
"ZY- 开头的指令编号,且签发者为霜砚(AG-SY-01)或冰朔(TCS-0002)"
],
"bypass": ["预览站", "天眼审核", "肥猫/桔子授权"],
"auditRequired": true,
"reason": "冰朔本人就是系统最高权限Level 3冰朔说话 = 最终授权"
},
"developerFlow": {
"description": "开发者(Level 1-2)提交的变更走完整S2审批流程",
"stages": ["预览站部署", "天眼审核", "授权人审批", "自动发布"]
},
"stages": [
{
"stage": 1,

View File

@ -1,8 +1,9 @@
/**
* 部署授权流程路由 · Phase 8
* 部署授权流程路由 · Phase 8 + S5 直通规则
*
* 天眼审核通过后 推送授权请求给授权人 授权人确认/拒绝 自动发布
* 系统绝不自动跳过授权步骤没有人类点确认就不发布
* 部署流分两条路径
* A) 冰朔直通TCS-0002 ZY- 指令签发 直接部署到正式站跳过预览/天眼/授权
* B) 开发者流程天眼审核 推送授权请求给授权人 确认/拒绝 自动发布
*
* POST /api/approval/request 创建授权请求天眼/系统内部调用
* POST /api/approval/:id/decide 授权人确认/拒绝
@ -25,6 +26,36 @@ var approversConfig = require('../config/approvers.json');
// ====== 内存中的授权记录(生产环境可迁移到持久化存储)======
var approvalStore = new Map();
// ====== S5 直通部署判断 ======
var autonomyEngine = require('../services/autonomy-engine');
/**
* 判断部署请求是否来自冰朔直通路径委托给 autonomy-engine 单一来源
*/
function isDirectDeploySource(user, body) {
var instructionId = body.instructionId || body.deployId || '';
var signedBy = body.signedBy || '';
return autonomyEngine.isDirectDeploySource(user.devId, instructionId, signedBy);
}
/**
* 写入部署日志直通和审批部署共用保持可追溯性
*/
function writeDeployLog(entry) {
var logDir = process.env.AUDIT_LOG_DIR || path.join(__dirname, '../../logs/audit');
try {
fs.mkdirSync(logDir, { recursive: true });
var today = new Date().toISOString().split('T')[0];
var logFile = path.join(logDir, 'deploy-' + today + '.jsonl');
fs.appendFile(logFile, JSON.stringify(entry) + '\n', function(err) {
if (err) console.error('部署日志写入失败:', err.message);
});
} catch (e) {
console.error('部署日志写入失败:', e.message);
}
}
// ====== 辅助函数 ======
/**
@ -60,6 +91,7 @@ router.use(auditMiddleware.auditLog);
/**
* 创建授权请求天眼审核通过后由系统调用
* S5: 冰朔直通路径 跳过审批直接部署到正式站
*/
router.post('/request', function(req, res) {
// 仅系统内部或管理员可创建授权请求
@ -85,6 +117,74 @@ router.post('/request', function(req, res) {
});
}
// ====== S5 直通判断 ======
if (isDirectDeploySource(req.user, body)) {
// 冰朔/霜砚指令 → 直接部署,不走审批流程
writeDeployLog({
action: 'direct_deploy',
deployId: deployId,
module: module,
channel: channel,
source: req.user.devId,
signedBy: body.signedBy || req.user.devId,
instructionId: body.instructionId || deployId,
reason: 'S5 冰朔直通部署规则:冰朔签发或口头下达的指令直接部署到正式站',
timestamp: new Date().toISOString()
});
// 直接触发正式站部署
var githubService;
try {
githubService = require('../services/github');
} catch (_) {
githubService = null;
}
if (githubService && githubService.triggerWorkflow) {
githubService.triggerWorkflow('deploy-to-server.yml', {
module: module,
deploy_id: deployId,
approved_by: req.user.devId,
target: 'production',
direct_deploy: 'true'
}).then(function() {
writeDeployLog({
action: 'direct_deploy_triggered',
deployId: deployId,
module: module,
source: req.user.devId,
timestamp: new Date().toISOString()
});
}).catch(function(err) {
console.error('直通部署触发失败:', err.message);
writeDeployLog({
action: 'direct_deploy_failed',
deployId: deployId,
error: err.message,
timestamp: new Date().toISOString()
});
});
}
return res.json({
success: true,
directDeploy: true,
deployId: deployId,
reply: '🚀 冰朔直通部署:' + module + ' 已直接触发正式站guanghulab.com部署。' +
'不经预览站、不经天眼审核、不经授权审批。部署日志已记录。'
});
}
// ====== 开发者流程:走完整 S2 审批 ======
if (!deployId || !module) {
return res.status(400).json({
error: true,
code: 'MISSING_FIELDS',
reply: '❌ 缺少必要字段deployId, module'
});
}
var approvers = getApprovers(channel);
var approvalId = 'APPROVAL-' + Date.now() + '-' + Math.random().toString(36).substring(2, 6);

View File

@ -113,11 +113,32 @@ function getDualLineConfig() {
return autonomyRules.dualLineArchitecture;
}
/**
* 判断请求是否来自冰朔直通部署路径 (S5)
* @param {string} devId - 开发者编号
* @param {string} instructionId - 指令编号
* @param {string} signedBy - 签发者编号
* @returns {boolean}
*/
function isDirectDeploySource(devId, instructionId, signedBy) {
// 冰朔本人
if (devId === 'TCS-0002') return true;
// ZY- 指令 + 可信签发者
if (instructionId && /^ZY-/.test(instructionId)) {
var trustedSigners = ['AG-SY-01', 'TCS-0002', 'ICE-0002'];
if (signedBy && trustedSigners.indexOf(signedBy) !== -1) return true;
}
return false;
}
module.exports = {
checkAutonomyCompliance: checkAutonomyCompliance,
detectInteractionMode: detectInteractionMode,
logAutonomousAction: logAutonomousAction,
getDeploymentStages: getDeploymentStages,
getDualLineConfig: getDualLineConfig,
writeAutonomyLog: writeAutonomyLog
writeAutonomyLog: writeAutonomyLog,
isDirectDeploySource: isDirectDeploySource
};

View File

@ -34,7 +34,9 @@ function startWatchdog() {
var activeLocks = executionLock.getActiveLocks();
var now = Date.now();
for (var [devId, lock] of activeLocks) {
for (var entry of activeLocks) {
var devId = entry[0];
var lock = entry[1];
if (lock.state !== 'running') continue;