@qijenchen/governance 0.1.0-beta.94
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/README.md +89 -0
- package/bin/attest.mjs +4 -0
- package/bin/check.mjs +4 -0
- package/bin/doctor.mjs +4 -0
- package/bin/generate.mjs +4 -0
- package/bin/governance.mjs +4 -0
- package/bin/hook.mjs +4 -0
- package/bin/upgrade.mjs +4 -0
- package/canonical/gates.json +42 -0
- package/canonical/manifest.json +476 -0
- package/canonical/plugin-aliases.json +25 -0
- package/canonical/provider-lifecycle.json +48 -0
- package/canonical/providers.json +455 -0
- package/canonical/roles.json +12 -0
- package/canonical/rules.json +81 -0
- package/canonical/schemas/attestation.schema.json +61 -0
- package/canonical/schemas/diagnostic.schema.json +37 -0
- package/canonical/schemas/gates.schema.json +27 -0
- package/canonical/schemas/lock.schema.json +189 -0
- package/canonical/schemas/manifest.schema.json +103 -0
- package/canonical/schemas/plugin-aliases.schema.json +59 -0
- package/canonical/schemas/provider-hook-coverage.schema.json +173 -0
- package/canonical/schemas/provider-lifecycle.schema.json +126 -0
- package/canonical/schemas/providers.schema.json +917 -0
- package/canonical/schemas/roles.schema.json +27 -0
- package/canonical/schemas/rules.schema.json +46 -0
- package/canonical/schemas/upgrade-plan.schema.json +31 -0
- package/package.json +42 -0
- package/src/authority-decision-evidence.mjs +413 -0
- package/src/canonical-order.mjs +8 -0
- package/src/carrier-projection.mjs +407 -0
- package/src/cli.mjs +114 -0
- package/src/closed-tool-execution.mjs +1001 -0
- package/src/common.mjs +278 -0
- package/src/contract.mjs +760 -0
- package/src/hook-api.mjs +107 -0
- package/src/index.mjs +14 -0
- package/src/provider-hook-normalization.mjs +1646 -0
- package/src/provider-review-binding.mjs +2377 -0
- package/src/snapshot.mjs +520 -0
package/src/hook-api.mjs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
canonicalize,
|
|
3
|
+
diagnostic,
|
|
4
|
+
humanResult,
|
|
5
|
+
readStdin,
|
|
6
|
+
result,
|
|
7
|
+
stableJson,
|
|
8
|
+
} from './common.mjs'
|
|
9
|
+
import { inspectContract } from './contract.mjs'
|
|
10
|
+
import { normalizeProviderHookInput, providerHookIssueMessage } from './provider-hook-normalization.mjs'
|
|
11
|
+
|
|
12
|
+
export function normalizeProviderEvent(contract, providerId, input) {
|
|
13
|
+
const provider = contract.providers.find((item) => item.id === providerId)
|
|
14
|
+
if (!provider) return canonicalize({ provider: null, event: '', tool: '', operation: 'read', paths: [], blocked: true, evidence: [] })
|
|
15
|
+
return canonicalize(normalizeProviderHookInput({ provider, input, repoRoot: contract.repoRoot }).normalized)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function evaluateHook({
|
|
19
|
+
provider,
|
|
20
|
+
input,
|
|
21
|
+
repoRoot = process.cwd(),
|
|
22
|
+
manifestPath,
|
|
23
|
+
role,
|
|
24
|
+
outputDirectory,
|
|
25
|
+
lockFile,
|
|
26
|
+
} = {}) {
|
|
27
|
+
const inspected = await inspectContract({ repoRoot, manifestPath, role, outputDirectory, lockFile, verifySources: false })
|
|
28
|
+
const diagnostics = [...inspected.diagnostics]
|
|
29
|
+
if (!inspected.contract || diagnostics.some((item) => ['FAIL', 'BLOCK', 'ERROR'].includes(item.outcome))) return { result: result('hook', diagnostics), exitCode: 2, normalized: null }
|
|
30
|
+
const contract = inspected.contract
|
|
31
|
+
const providerContract = contract.providers.find((item) => item.id === provider)
|
|
32
|
+
if (!providerContract) {
|
|
33
|
+
diagnostics.push(diagnostic({ ruleId: 'GOV-PROVIDER-001', severity: 'critical', kind: 'provider-unknown', path: provider || '', outcome: 'BLOCK', message: 'Hook provider is not registered.' }))
|
|
34
|
+
return { result: result('hook', diagnostics), exitCode: 2, normalized: null }
|
|
35
|
+
}
|
|
36
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
37
|
+
diagnostics.push(diagnostic({ ruleId: 'GOV-PROVIDER-001', severity: 'critical', kind: 'hook-input-invalid', path: provider, outcome: 'BLOCK', message: 'Hook input must be a JSON object.' }))
|
|
38
|
+
return { result: result('hook', diagnostics), exitCode: providerContract.blockingExitCode || 2, normalized: null }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const hookInput = normalizeProviderHookInput({ provider: providerContract, input, repoRoot: contract.repoRoot })
|
|
42
|
+
let normalized = canonicalize(hookInput.normalized)
|
|
43
|
+
for (const issue of hookInput.issues) {
|
|
44
|
+
diagnostics.push(diagnostic({
|
|
45
|
+
ruleId: 'GOV-PROVIDER-001',
|
|
46
|
+
severity: 'critical',
|
|
47
|
+
...issue.evidence,
|
|
48
|
+
outcome: 'BLOCK',
|
|
49
|
+
message: providerHookIssueMessage(issue),
|
|
50
|
+
}))
|
|
51
|
+
}
|
|
52
|
+
if (normalized.operation === 'write') {
|
|
53
|
+
const protectedPaths = [contract.lockFile, contract.outputDirectory]
|
|
54
|
+
const generatedRule = contract.rules.find((rule) => rule.ruleId === 'GOV-GENERATED-001')
|
|
55
|
+
for (const path of normalized.paths) {
|
|
56
|
+
const protectedPath = protectedPaths.find((candidate) => path === candidate || path.startsWith(`${candidate}/`))
|
|
57
|
+
if (!protectedPath) continue
|
|
58
|
+
diagnostics.push(diagnostic({
|
|
59
|
+
ruleId: generatedRule?.ruleId || 'GOV-GENERATED-001',
|
|
60
|
+
severity: generatedRule?.severity || 'critical',
|
|
61
|
+
kind: 'generated-artifact-write',
|
|
62
|
+
path,
|
|
63
|
+
expected: 'edit canonical source then regenerate',
|
|
64
|
+
actual: normalized.tool || normalized.event,
|
|
65
|
+
outcome: 'BLOCK',
|
|
66
|
+
message: 'Direct writes to generated governance artifacts are blocked.',
|
|
67
|
+
}))
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!diagnostics.length) diagnostics.push(diagnostic({ ruleId: 'GOV-GENERATED-001', severity: 'info', kind: 'hook-pass', path: normalized.paths.join(',') || '<none>', outcome: 'PASS', message: 'Normalized provider operation does not write a protected generated artifact.' }))
|
|
72
|
+
const blocked = diagnostics.some((item) => ['BLOCK', 'FAIL', 'ERROR'].includes(item.outcome))
|
|
73
|
+
const ordered = result('hook', diagnostics).diagnostics
|
|
74
|
+
normalized = canonicalize({
|
|
75
|
+
...normalized,
|
|
76
|
+
blocked,
|
|
77
|
+
evidence: ordered
|
|
78
|
+
.filter((item) => ['BLOCK', 'FAIL', 'ERROR'].includes(item.outcome))
|
|
79
|
+
.map((item) => item.evidence),
|
|
80
|
+
})
|
|
81
|
+
const value = result('hook', diagnostics, { provider, normalized })
|
|
82
|
+
return { result: value, exitCode: blocked ? (providerContract.blockingExitCode || 2) : 0, normalized }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function nativeHookStreams(evaluated) {
|
|
86
|
+
if (evaluated.exitCode === 0) return { stdout: '', stderr: '' }
|
|
87
|
+
return { stdout: '', stderr: `${humanResult(evaluated.result)}\n` }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function runHookCli({ provider, repoRoot, manifestPath, role, outputDirectory, lockFile, json = false } = {}) {
|
|
91
|
+
let input
|
|
92
|
+
try {
|
|
93
|
+
const raw = await readStdin()
|
|
94
|
+
input = raw.trim() ? JSON.parse(raw) : {}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
input = null
|
|
97
|
+
}
|
|
98
|
+
const evaluated = await evaluateHook({ provider, input, repoRoot, manifestPath, role, outputDirectory, lockFile })
|
|
99
|
+
if (json) process.stdout.write(stableJson(evaluated.result))
|
|
100
|
+
else {
|
|
101
|
+
const streams = nativeHookStreams(evaluated)
|
|
102
|
+
if (streams.stdout) process.stdout.write(streams.stdout)
|
|
103
|
+
if (streams.stderr) process.stderr.write(streams.stderr)
|
|
104
|
+
}
|
|
105
|
+
process.exitCode = evaluated.exitCode
|
|
106
|
+
return evaluated
|
|
107
|
+
}
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { inspectContract, inspectSkillParity, sourceRecords } from './contract.mjs'
|
|
2
|
+
export { evaluateHook, normalizeProviderEvent, runHookCli } from './hook-api.mjs'
|
|
3
|
+
export { normalizeProviderHookInput, providerHookIssueMessage } from './provider-hook-normalization.mjs'
|
|
4
|
+
export {
|
|
5
|
+
applyUpgrade,
|
|
6
|
+
attestRepository,
|
|
7
|
+
buildExpectedSnapshot,
|
|
8
|
+
checkRepository,
|
|
9
|
+
doctorRepository,
|
|
10
|
+
generateRepository,
|
|
11
|
+
planUpgrade,
|
|
12
|
+
snapshotState,
|
|
13
|
+
} from './snapshot.mjs'
|
|
14
|
+
export { canonicalize, diagnostic, digest, inventoryTree, stableJson, treeDigest } from './common.mjs'
|