Context
This sub-issue builds on the capability_ir module introduced in sub-issue 1 (file: pdd/prompts/capability_ir_python.prompt). It defines the PolicyBackend abstract interface, implements the Python AST backend that consumes the target-neutral ContractEffectIR, and adds an unsupported-target stub that returns a structured {"status": "unsupported"} response.
Interface Contract
This sub-issue imports ContractEffectIR and EffectItem from capability_ir (generated from pdd/prompts/capability_ir_python.prompt).
Expected function signature consumed from sub-issue 1:
from capability_ir import ContractEffectIR, EffectItem, parse_capabilities
Scope
Create the following new prompt/module files:
1. Backend interface — pdd/prompts/policy_backends/__init___python.prompt
Define:
class PolicyFinding:
severity: str # "error" | "warning" | "info"
effect: dict # {"action": ..., "resource": ...}
rule: str
message: str
location: dict # {"file": ..., "line": ..., "symbol": ...}
class PolicyBackend:
target: str
def analyze(self, artifact_path: str, contract_ir: ContractEffectIR) -> list[PolicyFinding]: ...
2. Python AST backend — pdd/prompts/policy_backends/python_ast_python.prompt
Implement PythonAstPolicyBackend(PolicyBackend) with target = "python". Moves Python-specific AST logic (imports scan, os.environ, smtplib, open(..., 'w'), logging patterns) from any existing checkup/contract_check code. Consumes ContractEffectIR.effects to determine which MUST_NOT rules to enforce.
Existing test fixtures at tests/fixtures/contract_check/ must continue to pass.
3. Backend registry — within pdd/prompts/policy_backends/__init___python.prompt
A get_backend(target: str) -> PolicyBackend function that:
- Returns
PythonAstPolicyBackend() for target="python"
- Raises or returns a stub result for unknown targets, containing
{"target": target, "status": "unsupported", "message": f"No policy backend registered for target: {target}"}.
Files to create/modify
pdd/prompts/policy_backends/__init___python.prompt (NEW)
pdd/prompts/policy_backends/python_ast_python.prompt (NEW)
Acceptance criteria
PolicyBackend ABC is defined with target: str and analyze(artifact_path, contract_ir) -> list[PolicyFinding]
PythonAstPolicyBackend.analyze() consumes ContractEffectIR (not raw capability strings)
get_backend('typescript') returns an unsupported-target indicator without raising an unhandled exception
- All existing tests in
tests/fixtures/contract_check/ continue to pass
- A test using a minimal non-Python stub (e.g., a
target='typescript') demonstrates the architecture is not Python-coupled
PDD Command Hint
change, sync
Split Contract
Command sequence: change → sync
Allowed write set:
pdd/prompts/policy_backends/__init___python.prompt
pdd/prompts/policy_backends/python_ast_python.prompt
Acceptance criteria:
- PolicyBackend ABC defined with target and analyze() method
- PythonAstPolicyBackend consumes ContractEffectIR from capability_ir module
- get_backend('typescript') returns unsupported-target dict without unhandled exception
- Existing contract_check test fixtures in tests/fixtures/contract_check/ continue to pass
- At least one test exercises a non-Python stub backend
Independently mergeable: True
Scope rule: Do not expand beyond this contract or implement sibling sub-issue work. If the contract is insufficient, report the gap instead.
PDD Command Hint: This is a new feature. Use change → sync (modify prompts, then generate and validate code).
Parent: #1370
Parent issue: #1370
Context
This sub-issue builds on the
capability_irmodule introduced in sub-issue 1 (file:pdd/prompts/capability_ir_python.prompt). It defines thePolicyBackendabstract interface, implements the Python AST backend that consumes the target-neutralContractEffectIR, and adds an unsupported-target stub that returns a structured{"status": "unsupported"}response.Interface Contract
This sub-issue imports
ContractEffectIRandEffectItemfromcapability_ir(generated frompdd/prompts/capability_ir_python.prompt).Expected function signature consumed from sub-issue 1:
Scope
Create the following new prompt/module files:
1. Backend interface —
pdd/prompts/policy_backends/__init___python.promptDefine:
2. Python AST backend —
pdd/prompts/policy_backends/python_ast_python.promptImplement
PythonAstPolicyBackend(PolicyBackend)withtarget = "python". Moves Python-specific AST logic (imports scan,os.environ,smtplib,open(..., 'w'), logging patterns) from any existing checkup/contract_check code. ConsumesContractEffectIR.effectsto determine which MUST_NOT rules to enforce.Existing test fixtures at
tests/fixtures/contract_check/must continue to pass.3. Backend registry — within
pdd/prompts/policy_backends/__init___python.promptA
get_backend(target: str) -> PolicyBackendfunction that:PythonAstPolicyBackend()fortarget="python"{"target": target, "status": "unsupported", "message": f"No policy backend registered for target: {target}"}.Files to create/modify
pdd/prompts/policy_backends/__init___python.prompt(NEW)pdd/prompts/policy_backends/python_ast_python.prompt(NEW)Acceptance criteria
PolicyBackendABC is defined withtarget: strandanalyze(artifact_path, contract_ir) -> list[PolicyFinding]PythonAstPolicyBackend.analyze()consumesContractEffectIR(not raw capability strings)get_backend('typescript')returns an unsupported-target indicator without raising an unhandled exceptiontests/fixtures/contract_check/continue to passtarget='typescript') demonstrates the architecture is not Python-coupledPDD Command Hint
change, sync
Split Contract
Command sequence: change → sync
Allowed write set:
pdd/prompts/policy_backends/__init___python.promptpdd/prompts/policy_backends/python_ast_python.promptAcceptance criteria:
Independently mergeable: True
Scope rule: Do not expand beyond this contract or implement sibling sub-issue work. If the contract is insufficient, report the gap instead.
PDD Command Hint: This is a new feature. Use
change → sync(modify prompts, then generate and validate code).Parent: #1370
Parent issue: #1370