feat: absorb phase13 templates rules clauses

This commit is contained in:
2026-05-22 11:30:37 +08:00
parent 462d12648f
commit ed2ce8a40d
17 changed files with 2524 additions and 2 deletions
@@ -184,6 +184,8 @@ with engine.connect() as conn:
"contract_template_versions",
"review_rules",
"review_rule_versions",
"standard_clauses",
"standard_clause_versions",
"prompt_assets",
"prompt_asset_versions",
"prompt_asset_run_links",
@@ -212,6 +214,14 @@ with engine.connect() as conn:
),
"review_rules_published": published_count(conn, "review_rules"),
"review_rule_versions": count_table(conn, "review_rule_versions"),
"standard_clauses": count_table(conn, "standard_clauses"),
"standard_clauses_active": (
count_table(conn, "standard_clauses", "is_active = true")
if has_column("standard_clauses", "is_active")
else 0
),
"standard_clauses_published": published_count(conn, "standard_clauses"),
"standard_clause_versions": count_table(conn, "standard_clause_versions"),
"prompt_assets": count_table(conn, "prompt_assets"),
"prompts_published": published_count(conn, "prompt_assets"),
"prompt_asset_versions": count_table(conn, "prompt_asset_versions"),
@@ -0,0 +1,167 @@
param(
[string]$RemoteHost = "192.168.3.90",
[string]$RemoteUser = "root",
[string]$BackendContainer = "ai-contract-backend-restore",
[string]$SshKeyPath = "$HOME\.ssh\id_rsa",
[switch]$UseSshpass,
[string]$TenantId = "tenant-demo-001",
[string]$UserId = "codex-autonomous-approval",
[string]$DisplayName = "CodexAutonomousApprovalMode",
[ValidateSet("none", "sqlite_phase13_assets")]
[string]$BootstrapMode = "none",
[switch]$DryRun,
[switch]$NoPublish,
[string]$ArtifactRoot = ""
)
$ErrorActionPreference = "Stop"
function Resolve-SshpassPath {
$command = Get-Command sshpass.exe -ErrorAction SilentlyContinue
if ($null -ne $command) {
return $command.Source
}
$candidates = @(
(Join-Path $env:LOCALAPPDATA "Microsoft\WinGet\Packages\xhcoding.sshpass-win32_Microsoft.Winget.Source_8wekyb3d8bbwe\sshpass.exe")
)
foreach ($candidate in $candidates) {
if (Test-Path $candidate) {
return $candidate
}
}
return $null
}
function ConvertTo-ShellSingleQuote {
param([string]$Value)
return "'" + ($Value -replace "'", "'`"`"`'") + "'"
}
function Invoke-LoggedCommand {
param(
[string]$Name,
[string]$FilePath,
[string[]]$Arguments,
[string]$LogDir
)
$logPath = Join-Path $LogDir "$Name.log"
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = "Continue"
try {
$output = & $FilePath @Arguments 2>&1
$exitCode = if ($LASTEXITCODE -is [int]) { $LASTEXITCODE } else { 0 }
} finally {
$ErrorActionPreference = $previousErrorActionPreference
}
$outputText = ($output -join [Environment]::NewLine)
$outputText | Set-Content -Encoding UTF8 -Path $logPath
if ($exitCode -ne 0) {
throw "$Name failed with exit code $exitCode. See $logPath"
}
return $outputText
}
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
if (-not (Test-Path "$HOME\.Codex\skills\gstack\bin")) {
throw "GSTACK_MISSING: gstack is required for AI-assisted work in this repo."
}
if ([string]::IsNullOrWhiteSpace($ArtifactRoot)) {
$ArtifactRoot = Join-Path $repoRoot "project-docs\qa-artifacts\phase13-template-rule-clause-absorption"
}
$timestamp = Get-Date -Format "yyyy-MM-ddTHH-mm-ss"
$artifactDir = Join-Path $ArtifactRoot $timestamp
New-Item -ItemType Directory -Force -Path $artifactDir | Out-Null
$seedToolPath = Join-Path $repoRoot "backend\tools\seed_phase13_template_rule_clause_assets.py"
$phase13LibraryPath = Join-Path $repoRoot "project-docs\phase13-legal-knowledge-template-library"
foreach ($requiredPath in @($seedToolPath, $phase13LibraryPath)) {
if (-not (Test-Path -LiteralPath $requiredPath -PathType Leaf)) {
if (-not (Test-Path -LiteralPath $requiredPath -PathType Container)) {
throw "Required input path does not exist: $requiredPath"
}
}
}
$authMode = ""
$sshpassPath = $null
if ($UseSshpass -or -not [string]::IsNullOrWhiteSpace($env:SSHPASS)) {
$sshpassPath = Resolve-SshpassPath
if ([string]::IsNullOrWhiteSpace($sshpassPath)) {
throw "sshpass.exe not found. Install sshpass or use SSH key auth."
}
if ([string]::IsNullOrWhiteSpace($env:SSHPASS)) {
throw "SSHPASS environment variable is empty."
}
$authMode = "sshpass"
} elseif (Test-Path $SshKeyPath) {
$authMode = "key"
} else {
throw "SSH key not found and SSHPASS is not configured."
}
$target = "${RemoteUser}@${RemoteHost}"
$remoteWorkDir = "/tmp/ai-contract-phase13-absorption-$timestamp"
$containerScript = "$remoteWorkDir/seed_phase13_template_rule_clause_assets.py"
$containerRepoRoot = "$remoteWorkDir/repo"
$containerLibraryRoot = "$containerRepoRoot/project-docs/phase13-legal-knowledge-template-library"
$containerOutput = "$remoteWorkDir/output"
if ($authMode -eq "sshpass") {
$sshFile = $sshpassPath
$sshPrefix = @("-e", "ssh")
$scpPrefix = @("-e", "scp")
} else {
$sshFile = "ssh"
$sshPrefix = @("-i", $SshKeyPath)
$scpPrefix = @("-i", $SshKeyPath)
}
$sshCommon = @("-o", "StrictHostKeyChecking=accept-new", "-o", "ConnectTimeout=10")
$scpCommon = @("-o", "StrictHostKeyChecking=accept-new", "-o", "ConnectTimeout=10")
if ($authMode -eq "key") {
$sshCommon = @("-o", "BatchMode=yes") + $sshCommon
$scpCommon = @("-o", "BatchMode=yes") + $scpCommon
}
Invoke-LoggedCommand -Name "remote-mkdir" -FilePath $sshFile -Arguments ($sshPrefix + $sshCommon + @($target, "mkdir -p $(ConvertTo-ShellSingleQuote $remoteWorkDir) $(ConvertTo-ShellSingleQuote "$remoteWorkDir/project-docs")")) -LogDir $artifactDir | Out-Null
Invoke-LoggedCommand -Name "scp-seed-tool" -FilePath $(if ($authMode -eq "sshpass") { $sshpassPath } else { "scp" }) -Arguments ($scpPrefix + $scpCommon + @($seedToolPath, "${target}:$remoteWorkDir/seed_phase13_template_rule_clause_assets.py")) -LogDir $artifactDir | Out-Null
Invoke-LoggedCommand -Name "scp-phase13-library" -FilePath $(if ($authMode -eq "sshpass") { $sshpassPath } else { "scp" }) -Arguments ($scpPrefix + $scpCommon + @("-r", $phase13LibraryPath, "${target}:$remoteWorkDir/project-docs/")) -LogDir $artifactDir | Out-Null
$remoteCommandParts = @(
"docker exec $BackendContainer sh -lc $(ConvertTo-ShellSingleQuote "rm -rf $remoteWorkDir && mkdir -p $remoteWorkDir $containerRepoRoot $containerRepoRoot/project-docs")",
"docker cp $remoteWorkDir/seed_phase13_template_rule_clause_assets.py ${BackendContainer}:$containerScript",
"docker cp $remoteWorkDir/project-docs/phase13-legal-knowledge-template-library ${BackendContainer}:$containerRepoRoot/project-docs/phase13-legal-knowledge-template-library"
)
$seedArgs = @(
"PYTHONPATH=/app python $(ConvertTo-ShellSingleQuote $containerScript)",
"--repo-root $(ConvertTo-ShellSingleQuote $containerRepoRoot)",
"--library-root $(ConvertTo-ShellSingleQuote $containerLibraryRoot)",
"--output-dir $(ConvertTo-ShellSingleQuote $containerOutput)",
"--tenant-id $(ConvertTo-ShellSingleQuote $TenantId)",
"--user-id $(ConvertTo-ShellSingleQuote $UserId)",
"--display-name $(ConvertTo-ShellSingleQuote $DisplayName)",
"--bootstrap-mode $(ConvertTo-ShellSingleQuote $BootstrapMode)"
)
if ($DryRun) {
$seedArgs += "--dry-run"
}
if ($NoPublish) {
$seedArgs += "--no-publish"
}
$seedCommand = "cd /app && " + ($seedArgs -join " ")
$remoteCommandParts += "docker exec $BackendContainer sh -lc $(ConvertTo-ShellSingleQuote $seedCommand)"
$remoteCommandParts += "docker cp ${BackendContainer}:$containerOutput $remoteWorkDir/output"
$remoteCommand = $remoteCommandParts -join " && "
Invoke-LoggedCommand -Name "remote-seed-phase13-assets" -FilePath $sshFile -Arguments ($sshPrefix + $sshCommon + @($target, $remoteCommand)) -LogDir $artifactDir | Out-Null
Invoke-LoggedCommand -Name "scp-seed-output" -FilePath $(if ($authMode -eq "sshpass") { $sshpassPath } else { "scp" }) -Arguments ($scpPrefix + $scpCommon + @("-r", "${target}:$remoteWorkDir/output/.", $artifactDir)) -LogDir $artifactDir | Out-Null
Write-Output "PHASE13_TEMPLATE_RULE_CLAUSE_ABSORPTION_PASS $artifactDir"