Add blocked Stage 5 target admission record
This commit is contained in:
parent
f8a6d6873d
commit
3f8a2eeeab
@ -55,7 +55,7 @@ Type: HITL
|
||||
|
||||
Status: blocked.
|
||||
|
||||
Blocked by: CTO-WORK-037, CTO-WORK-039, and explicit JP selection or approval of an owned low-risk noncritical Target Repository.
|
||||
Blocked by: CTO-WORK-037, CTO-WORK-039, CTO-WORK-040, and explicit JP selection or approval of an owned low-risk noncritical Target Repository.
|
||||
|
||||
User stories covered: CTO Case Candidate Backend PRD stories 4, 5, 7, 8, 9, 10, 11, 13.
|
||||
|
||||
@ -112,6 +112,41 @@ Validator: `python3 tools/validate_cto_child.py`
|
||||
|
||||
Done evidence: template artifact, issue reference, validator JSON, clean worktree, commit.
|
||||
|
||||
|
||||
### CTO-WORK-040 - Stage 5 Target Repository Admission Record
|
||||
|
||||
Type: HITL
|
||||
|
||||
Status: blocked.
|
||||
|
||||
Blocked by: CTO-WORK-039 and explicit JP selection or approval of an owned low-risk noncritical Target Repository.
|
||||
|
||||
User stories covered: CTO Case Candidate Backend PRD stories 4, 5, 7, 8, 9, 10, 11, 13.
|
||||
|
||||
What to build: Maintain the concrete Stage 5 Target Repository admission record. The current record is intentionally `not_admitted` and blocks Stage 5 execution until JP supplies the repository path, ownership proof, noncritical rationale, allowed paths, forbidden paths, and approval metadata.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- [x] Admission record exists as JSON.
|
||||
- [x] Admission record status is `not_admitted` by default.
|
||||
- [x] Admission record contains no repository path until JP approves one.
|
||||
- [x] Admission record contains no secrets or credentials.
|
||||
- [x] Admission record includes all required forbidden actions.
|
||||
- [x] Admission record requires operator outcome.
|
||||
- [x] Local CTO validator checks the safe blocked record state.
|
||||
- [ ] JP supplies an owned low-risk noncritical repository path.
|
||||
- [ ] JP supplies ownership evidence.
|
||||
- [ ] JP supplies noncritical rationale.
|
||||
- [ ] JP supplies allowed paths and forbidden paths.
|
||||
- [ ] JP supplies approval source and approval timestamp.
|
||||
- [ ] Admission record is updated to `admitted` only after all required fields are present.
|
||||
|
||||
Allowed files: CTO child workspace planning docs and local validator only until a concrete Target Repository is approved.
|
||||
|
||||
Validator: `python3 tools/validate_cto_child.py`
|
||||
|
||||
Done evidence for current blocked state: admission JSON, issue reference, validator JSON, clean worktree, commit.
|
||||
|
||||
## Granularity Check
|
||||
|
||||
This is intentionally two slices: one planning route and one executable harness route. Stage 5 is not over-granular because it is the first proof involving an admitted owned repository and must separate repository ownership, approval, allowed paths, verification, and operator outcome before default candidacy.
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
{
|
||||
"admission_status": "not_admitted",
|
||||
"target_repository_path": "",
|
||||
"repository_owner": "",
|
||||
"ownership_evidence": "",
|
||||
"risk_classification": "",
|
||||
"noncritical_rationale": "",
|
||||
"allowed_paths": [],
|
||||
"forbidden_paths": [],
|
||||
"forbidden_actions": [
|
||||
"push",
|
||||
"merge",
|
||||
"deploy",
|
||||
"close",
|
||||
"pr_open",
|
||||
"issue_close",
|
||||
"public_publication",
|
||||
"credential_change",
|
||||
"vendor_source_mutation",
|
||||
"cortex_core_mutation"
|
||||
],
|
||||
"approval_source": "",
|
||||
"approval_timestamp": "",
|
||||
"operator_outcome_required": true,
|
||||
"review_trigger": "before Stage 5 execution, before target repository path change, before allowed path change, before forbidden action change, before risk classification change"
|
||||
}
|
||||
@ -49,6 +49,7 @@ This workspace is registered as a child-local planning workspace. Registration d
|
||||
| |-- CTO-CASE-STAGE5-OWNED-NONCRITICAL-REPO-PRD.md
|
||||
| |-- CTO-CASE-STAGE5-OWNED-NONCRITICAL-REPO-ISSUES.md
|
||||
| |-- CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION-TEMPLATE.md
|
||||
| |-- CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION.json
|
||||
| |-- CTO-CASE-PROVIDER-ADMISSION-PRD.md
|
||||
| |-- CTO-CASE-PROVIDER-ADMISSION-ISSUES.md
|
||||
| |-- CTO-CASE-PROVIDER-BUILD-PRD.md
|
||||
|
||||
@ -195,3 +195,8 @@ items:
|
||||
status: validated
|
||||
source: .sot/03-PROTOCOLS/CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION-TEMPLATE.md
|
||||
owner: ""
|
||||
- id: CTO-WORK-040
|
||||
title: Stage 5 Target Repository Admission Record
|
||||
status: blocked
|
||||
source: .sot/03-PROTOCOLS/CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION.json
|
||||
owner: jp
|
||||
|
||||
@ -35,6 +35,7 @@ REQUIRED_FILES = [
|
||||
".sot/03-PROTOCOLS/CTO-CASE-STAGE5-OWNED-NONCRITICAL-REPO-PRD.md",
|
||||
".sot/03-PROTOCOLS/CTO-CASE-STAGE5-OWNED-NONCRITICAL-REPO-ISSUES.md",
|
||||
".sot/03-PROTOCOLS/CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION-TEMPLATE.md",
|
||||
".sot/03-PROTOCOLS/CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION.json",
|
||||
".sot/03-PROTOCOLS/CTO-CASE-PROVIDER-ADMISSION-PRD.md",
|
||||
".sot/03-PROTOCOLS/CTO-CASE-PROVIDER-ADMISSION-ISSUES.md",
|
||||
".sot/03-PROTOCOLS/CTO-CASE-PROVIDER-BUILD-PRD.md",
|
||||
@ -334,6 +335,7 @@ REQUIRED_STAGE5_ISSUE_IDS = [
|
||||
"CTO-WORK-037",
|
||||
"CTO-WORK-038",
|
||||
"CTO-WORK-039",
|
||||
"CTO-WORK-040",
|
||||
]
|
||||
|
||||
REQUIRED_STAGE5_TARGET_ADMISSION_TEMPLATE_PHRASES = [
|
||||
@ -355,6 +357,31 @@ REQUIRED_STAGE5_TARGET_ADMISSION_TEMPLATE_PHRASES = [
|
||||
"This template does not authorize owned repository mutation.",
|
||||
]
|
||||
|
||||
REQUIRED_STAGE5_TARGET_ADMISSION_JSON = {
|
||||
"admission_status": "not_admitted",
|
||||
"target_repository_path": "",
|
||||
"repository_owner": "",
|
||||
"ownership_evidence": "",
|
||||
"risk_classification": "",
|
||||
"noncritical_rationale": "",
|
||||
"approval_source": "",
|
||||
"approval_timestamp": "",
|
||||
"operator_outcome_required": True,
|
||||
}
|
||||
|
||||
REQUIRED_STAGE5_TARGET_FORBIDDEN_ACTIONS = [
|
||||
"push",
|
||||
"merge",
|
||||
"deploy",
|
||||
"close",
|
||||
"pr_open",
|
||||
"issue_close",
|
||||
"public_publication",
|
||||
"credential_change",
|
||||
"vendor_source_mutation",
|
||||
"cortex_core_mutation",
|
||||
]
|
||||
|
||||
REQUIRED_PROVIDER_ADMISSION_PRD_PHRASES = [
|
||||
"Local planning SOT only. Not a Core Protocol. Not active Core authority.",
|
||||
"https://github.com/workos/case.git",
|
||||
@ -1027,6 +1054,40 @@ def main() -> int:
|
||||
if phrase not in text:
|
||||
errors.append(f"missing_stage5_target_admission_template_phrase:{phrase}")
|
||||
|
||||
stage5_target_admission = ROOT / ".sot/03-PROTOCOLS/CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION.json"
|
||||
if stage5_target_admission.is_file():
|
||||
checked.append("stage5_target_admission_json:parse")
|
||||
try:
|
||||
payload = json.loads(stage5_target_admission.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as exc:
|
||||
errors.append(f"stage5_target_admission_invalid_json:{exc}")
|
||||
payload = {}
|
||||
if not isinstance(payload, dict):
|
||||
errors.append("stage5_target_admission_must_be_object")
|
||||
payload = {}
|
||||
for key, expected in REQUIRED_STAGE5_TARGET_ADMISSION_JSON.items():
|
||||
checked.append(f"stage5_target_admission_json:{key}")
|
||||
if payload.get(key) != expected:
|
||||
errors.append(f"stage5_target_admission_mismatch:{key}:expected_{expected}:actual_{payload.get(key)}")
|
||||
for key in ["allowed_paths", "forbidden_paths", "forbidden_actions"]:
|
||||
checked.append(f"stage5_target_admission_json_list:{key}")
|
||||
if not isinstance(payload.get(key), list):
|
||||
errors.append(f"stage5_target_admission_missing_list:{key}")
|
||||
forbidden_actions = payload.get("forbidden_actions", [])
|
||||
if isinstance(forbidden_actions, list):
|
||||
for action in REQUIRED_STAGE5_TARGET_FORBIDDEN_ACTIONS:
|
||||
checked.append(f"stage5_target_admission_forbidden_action:{action}")
|
||||
if action not in forbidden_actions:
|
||||
errors.append(f"stage5_target_admission_missing_forbidden_action:{action}")
|
||||
if payload.get("allowed_paths") != []:
|
||||
errors.append("stage5_target_admission_allowed_paths_must_be_empty_while_not_admitted")
|
||||
if not isinstance(payload.get("review_trigger"), str) or not payload.get("review_trigger"):
|
||||
errors.append("stage5_target_admission_missing_review_trigger")
|
||||
for key in payload:
|
||||
checked.append(f"stage5_target_admission_json_secret_key:{key}")
|
||||
if key.lower() in {"api_key", "apikey", "access_token", "token", "secret", "password", "credential_value"}:
|
||||
errors.append(f"stage5_target_admission_forbidden_secret_key:{key}")
|
||||
|
||||
provider_admission_prd = ROOT / ".sot/03-PROTOCOLS/CTO-CASE-PROVIDER-ADMISSION-PRD.md"
|
||||
if provider_admission_prd.is_file():
|
||||
text = provider_admission_prd.read_text(encoding="utf-8")
|
||||
@ -1286,6 +1347,7 @@ def main() -> int:
|
||||
"CTO-WORK-037": "validated",
|
||||
"CTO-WORK-038": "blocked",
|
||||
"CTO-WORK-039": "validated",
|
||||
"CTO-WORK-040": "blocked",
|
||||
}
|
||||
for issue_id, expected in expected_statuses.items():
|
||||
checked.append(f"workboard_status:{issue_id}:{expected}")
|
||||
@ -1326,6 +1388,8 @@ def main() -> int:
|
||||
errors.append("workboard_missing_stage5_issues_source")
|
||||
if "CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION-TEMPLATE.md" not in text:
|
||||
errors.append("workboard_missing_stage5_target_admission_template_source")
|
||||
if "CTO-CASE-STAGE5-TARGET-REPOSITORY-ADMISSION.json" not in text:
|
||||
errors.append("workboard_missing_stage5_target_admission_json_source")
|
||||
if "CTO-CASE-PROVIDER-ADMISSION-PRD.md" not in text:
|
||||
errors.append("workboard_missing_provider_admission_prd_source")
|
||||
if "CTO-CASE-PROVIDER-ADMISSION-ISSUES.md" not in text:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user