Skip to content

Commit ed86cfa

Browse files
authored
Merge pull request #6 from libis/ai/bootstrap-20250916
chore(ai): bootstrap governance workflows and PR template
2 parents 65153d4 + 5d10602 commit ed86cfa

16 files changed

Lines changed: 2046 additions & 0 deletions

.github/CODEOWNERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# CODEOWNERS — interim owners for governance and workflows
2+
# Replace with your org/team handles when available (e.g., @libis/security @libis/privacy).
3+
4+
/governance/* @ErykKul
5+
/governance/** @ErykKul
6+
/policies/* @ErykKul
7+
/.github/workflows/* @ErykKul

.github/ai-transition-sync.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"source_repo": "libis/ai-transition",
3+
"source_ref": "main",
4+
"synced_at": "2025-09-16T00:00:00Z",
5+
"files_copied": [],
6+
"upstream_commit": "unknown"
7+
}

.github/pull_request_template.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<!-- @ai-generated: true -->
2+
3+
# @ai-tool: Copilot
4+
5+
## Summary
6+
7+
<!-- Provide 1–3 bullets: what changed and why; link issues like #123 or full URLs. Do not leave this placeholder. -->
8+
9+
## AI Provenance (required for AI-assisted changes)
10+
11+
- Prompt: <!-- paste exact prompt or link -->
12+
- Model: <!-- e.g., GitHub Copilot gpt-5 -->
13+
- Date: <!-- UTC ISO-8601, e.g., 2025-09-12T14:23:45Z; NEVER ${...} -->
14+
- Author: <!-- @handle only (no names/emails) -->
15+
- Role: provider|deployer
16+
17+
## Compliance checklist
18+
19+
- [ ] No secrets/PII
20+
- [ ] Transparency notice updated (if user-facing)
21+
- [ ] Agent logging enabled (actions/decisions logged)
22+
- [ ] Kill-switch / feature flag present for AI features
23+
- [ ] No prohibited practices under EU AI Act
24+
- [ ] Human oversight retained (required if high-risk or agent mode)
25+
- Risk classification: limited|high
26+
- Personal data: yes|no
27+
- DPIA: <link or N/A>
28+
- Automated decision-making: yes|no
29+
- Agent mode used: yes|no
30+
- GPAI obligations: <link or N/A> (if Role: provider)
31+
- Vendor GPAI compliance reviewed: <link or N/A> (if Role: deployer)
32+
- [ ] License/IP attestation
33+
- Attribution: <link or N/A>
34+
- Oversight plan: <link> (required if high-risk/ADM)
35+
36+
<!-- PLACEHOLDER-LINKS: Automation (/gov links, /gov autofill apply) will replace <link or N/A> values. -->
37+
> Tip: comment '/gov' to run checks + Copilot review, and '/gov links' to preview suggested links.
38+
39+
### Change-type specifics
40+
41+
- Security review: <link> or check: [ ] Security review requested (required if auth/permissions/etc.)
42+
- Media assets changed:
43+
- [ ] AI content labeled
44+
- C2PA: <link or N/A>
45+
- UI changed:
46+
- [ ] Accessibility review (EN 301 549/WCAG)
47+
- Accessibility statement: <link or N/A>
48+
- Deploy/infra changed:
49+
- Privacy notice: <link>
50+
- Lawful basis: <e.g., public task/consent/contract or N/A>
51+
- Retention schedule: <link or N/A>
52+
- NIS2 applicability: yes|no|N/A
53+
- Incident response plan: <link if NIS2=yes>
54+
- Backend/API changed:
55+
- ASVS: <link> or check [ ] OWASP ASVS review
56+
- Log retention policy: <link or N/A>
57+
- Data paths changed:
58+
- TDM: yes|no|N/A
59+
- TDM compliance: <link if yes>
60+
61+
## Tests & Risk
62+
63+
- [ ] Unit/integration tests added/updated
64+
- [ ] Security scan passed
65+
- Rollback plan: <summary>
66+
- Smoke test: <link>
67+
- [ ] Docs updated (if needed)

.github/workflows/ai-agent.yml

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# @ai-generated: true
2+
# @ai-tool: Copilot
3+
name: AI Governance Agent (ChatOps)
4+
5+
on:
6+
issue_comment:
7+
types: [created]
8+
9+
permissions:
10+
contents: read
11+
issues: write
12+
pull-requests: write
13+
14+
jobs:
15+
respond:
16+
name: Respond to /gov commands on PRs
17+
if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/gov') }}
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Handle /gov command
21+
uses: actions/github-script@v7
22+
with:
23+
github-token: ${{ secrets.GITHUB_TOKEN }}
24+
script: |
25+
const body = context.payload.comment.body.trim();
26+
const owner = context.repo.owner;
27+
const repo = context.repo.repo;
28+
const issue_number = context.payload.issue.number;
29+
30+
const helpText =
31+
'AI Governance Agent commands:\\n\\n\n' +
32+
`- /gov help — show this help\n` +
33+
`- /gov check — scan this PR for missing governance checklist items and summarize changes\n` +
34+
`- /gov copilot — ask GitHub Copilot to review this PR\n` +
35+
`- /gov links — preview suggested links (governance/test runs) for the PR template\n` +
36+
`- /gov autofill apply — auto-fill safe N/A defaults and add run links into the PR body\n` +
37+
`- /gov — run default check, trigger Copilot review, preview links, and auto-apply autofill\n`;
38+
39+
const isHelp = body.match(/^\/gov\s+help\b/i);
40+
const isCheck = body.match(/^\/gov\s+check\b/i);
41+
const isBare = body.match(/^\/gov\s*$/i);
42+
43+
if (!isHelp && !isCheck && !isBare) {
44+
return; // Ignore other /gov variants for now
45+
}
46+
47+
if (isHelp) {
48+
await github.rest.issues.createComment({ owner, repo, issue_number, body: helpText });
49+
return;
50+
}
51+
52+
// Fetch PR details and changed files (treat bare /gov as /gov check)
53+
const doCheck = isCheck || isBare;
54+
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
55+
const prBody = (pr.body || '').toString();
56+
const files = await github.paginate(github.rest.pulls.listFiles, { owner, repo, pull_number: issue_number, per_page: 100 });
57+
58+
// Heuristics for change types
59+
const changedPaths = files.map(f => f.filename);
60+
const rx = {
61+
userUI: /(^|\/)(ui|web|frontend|public|templates)(\/|$)|(^|\/)src\/.*\.(html|tsx?|vue)$/i,
62+
sensitive: /(^|\/)(auth|authn|authz|login|acl|permissions?|access[_-]?control|secrets?|tokens?|jwt|oauth)(\/|$)|\.(policy|rego)$/i,
63+
infra: /(^|\/)(k8s|kubernetes|helm|charts|deploy|ops|infra|infrastructure|manifests|terraform|ansible)(\/|$)|(^|\/)dockerfile$|docker-compose\.ya?ml$|Chart\.ya?ml$/i,
64+
backend: /(^|\/)(src|api|server|backend|app)(\/|\/.*)([^\/]+)\.(js|ts|py|rb|go|java|cs)$/i,
65+
media: /\.(png|jpe?g|gif|webp|svg|mp4|mp3|wav|pdf)$/i,
66+
data: /(^|\/)(data|datasets|training|notebooks|scripts)(\/|$)/i
67+
};
68+
const has = (re) => changedPaths.some(p => re.test(p));
69+
const flags = {
70+
userUI: has(rx.userUI),
71+
sensitive: has(rx.sensitive),
72+
infra: has(rx.infra),
73+
backend: has(rx.backend),
74+
media: has(rx.media),
75+
data: has(rx.data)
76+
};
77+
78+
// Simple PR body checks mirroring the reusable workflow
79+
const missing = [];
80+
const need = (label, ok) => { if (!ok) missing.push(label); };
81+
82+
need('Prompt', /Prompt/i.test(prBody));
83+
need('Model', /Model/i.test(prBody));
84+
need('Date', /Date/i.test(prBody));
85+
need('Author', /Author/i.test(prBody));
86+
need('[x] No secrets/PII', /\[x\].*no\s+secrets\/?pii|no\s+pii\/?secrets/i.test(prBody));
87+
need('Risk classification: limited|high', /Risk\s*classification:\s*(limited|high)/i.test(prBody));
88+
need('Personal data: yes|no', /Personal\s*data:\s*(yes|no)/i.test(prBody));
89+
need('Automated decision-making: yes|no', /Automated\s*decision-?making:\s*(yes|no)/i.test(prBody));
90+
need('Agent mode used: yes|no', /Agent\s*mode\s*used:\s*(yes|no)/i.test(prBody));
91+
need('Role: provider|deployer', /Role:\s*(provider|deployer)/i.test(prBody));
92+
93+
if (flags.userUI) {
94+
need('[x] Transparency notice updated', /\[x\].*transparency\s+notice/i.test(prBody));
95+
need('Accessibility statement: <link or N/A>', /Accessibility\s*statement:\s*(https?:\/\/|N\/?A)/i.test(prBody));
96+
}
97+
if (flags.media) {
98+
need('[x] AI content labeled', /\[x\].*ai\s*content\s*labeled/i.test(prBody));
99+
need('C2PA: <link or N/A>', /C2PA:\s*(https?:\/\/|N\/?A)/i.test(prBody));
100+
}
101+
if (flags.infra) {
102+
need('Privacy notice: <link>', /Privacy\s*notice:\s*(https?:\/\/)/i.test(prBody));
103+
need('Lawful basis: <text or N/A>', /Lawful\s*basis:\s*([A-Za-z]+|N\/?A)/i.test(prBody));
104+
need('Retention schedule: <link or N/A>', /Retention\s*schedule:\s*(https?:\/\/|N\/?A)/i.test(prBody));
105+
}
106+
if (flags.backend) {
107+
need('[x] OWASP ASVS review or ASVS: <link>', /\[x\].*owasp\s*asvs|ASVS:\s*(https?:\/\/)/i.test(prBody));
108+
}
109+
110+
// Build a concise response
111+
const bullet = (b) => `- ${b}`;
112+
const filesList = changedPaths.slice(0, 50).map(bullet).join('\n');
113+
const missingList = missing.length ? missing.map(bullet).join('\n') : '- None (looks good)';
114+
const flagsList = Object.entries(flags).filter(([,v]) => v).map(([k]) => `
115+
- ${k}`).join('') || '\n - none detected';
116+
117+
const reply =
118+
'### Governance Agent Report\\n\\n\n' +
119+
`PR: #${issue_number} by @${pr.user.login}\n\n` +
120+
`Changed files (${changedPaths.length}):\n${filesList}\n\n` +
121+
`Detected change types:${flagsList}\n\n` +
122+
`Missing or incomplete items:\n${missingList}\n\n` +
123+
`Tip: Use the PR template fields to satisfy these checks.\n\n` +
124+
`Run /gov help for commands. Also try: /gov links and /gov autofill apply.`;
125+
126+
if (doCheck) {
127+
await github.rest.issues.createComment({ owner, repo, issue_number, body: reply });
128+
}
129+
130+
// If the command was bare /gov, also trigger Copilot review, links preview, and auto-apply autofill
131+
if (isBare) {
132+
await github.rest.issues.createComment({ owner, repo, issue_number, body: '/gov copilot' });
133+
// And trigger auto-links preview so contributors can quickly fill PR fields
134+
await github.rest.issues.createComment({ owner, repo, issue_number, body: '/gov links' });
135+
// Finally, auto-apply link autofill (safe defaults + run links)
136+
await github.rest.issues.createComment({ owner, repo, issue_number, body: '/gov autofill apply' });
137+
}

0 commit comments

Comments
 (0)