From e2aa3f9010a2ecaff075839e915ab09d19e1649d Mon Sep 17 00:00:00 2001 From: Thorsten Hindermann Date: Thu, 23 Jul 2026 19:46:29 +0200 Subject: [PATCH 1/2] chore: harden intake review series evidence --- .../speckit-intake-review-status/SKILL.md | 1 + .agents/skills/speckit-intake-review/SKILL.md | 4 +- .../speckit-intake-review-status/SKILL.md | 3 +- .claude/skills/speckit-intake-review/SKILL.md | 6 +- .../speckit.intake-review-status.agent.md | 3 +- .github/agents/speckit.intake-review.agent.md | 6 +- .../command/speckit.intake-review-status.md | 3 +- .opencode/command/speckit.intake-review.md | 6 +- .specify/presets/.registry | 90 +++---- .../intake-review-governance/README.md | 14 +- .../commands/speckit.intake-review-status.md | 1 + .../commands/speckit.intake-review.md | 4 +- .../intake-review-governance/docs/README.md | 4 + .../docs/field-validation-summary.md | 37 ++- .../docs/man/validate-intake-review-result.1 | 7 +- .../intake-review-governance/preset.yml | 2 +- .../scripts/validate-intake-review-result.ps1 | 223 +++++++++++++++++- .../scripts/validate-intake-review-result.sh | 149 +++++++++++- .../templates/agent-file-addendum.md | 3 + .../templates/field-validation-summary.md | 5 + .../intake-review-checklist-template.md | 4 + .../intake-review-request-template.json | 4 +- .../intake-review-result-template.json | 6 +- .../intake-review-runbook-template.md | 14 ++ .../tests/test-intake-review-validator.ps1 | 173 +++++++++++++- ...t-intake-authoring-governance-presets.json | 2 +- ...-kit-intake-review-governance-presets.json | 2 +- scripts/templates/AGENTS.md.tmpl | 4 +- scripts/templates/CLAUDE.md.tmpl | 4 +- scripts/templates/GEMINI.md.tmpl | 4 +- scripts/templates/copilot-instructions.tmpl | 4 +- scripts/templates/speckit-workflow-section.md | 11 +- .../intake-review-report.md | 34 +++ .../intake-review-request.json | 83 +++++++ .../intake-review-result.json | 126 ++++++++++ .../intake-review-report.md | 16 ++ .../intake-review-request.json | 70 +++++- .../intake-review-result.json | 14 +- 38 files changed, 1051 insertions(+), 95 deletions(-) create mode 100644 specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-report.md create mode 100644 specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-request.json create mode 100644 specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-result.json diff --git a/.agents/skills/speckit-intake-review-status/SKILL.md b/.agents/skills/speckit-intake-review-status/SKILL.md index acbe726..81d7245 100644 --- a/.agents/skills/speckit-intake-review-status/SKILL.md +++ b/.agents/skills/speckit-intake-review-status/SKILL.md @@ -23,6 +23,7 @@ invoke the installed validator with the repository root, and report: - target paths, normalized hashes, optional Git blobs, and drift; - findings, questions, accepted-risk ownership, and re-evaluation triggers; - series and campaign coverage, including unique intake and worker counts; +- for Series mode, request hash, root set, target order, edge count, and DAG validity; - whether Preset 7 or Preset 8 may consume the result under current policy; - the exact next command. diff --git a/.agents/skills/speckit-intake-review/SKILL.md b/.agents/skills/speckit-intake-review/SKILL.md index a094664..630e2f2 100644 --- a/.agents/skills/speckit-intake-review/SKILL.md +++ b/.agents/skills/speckit-intake-review/SKILL.md @@ -27,7 +27,9 @@ exist. Use the installed policy and checklist. evidence, delivery authority, risks, references, prompt alignment, and secret or unnecessary-personal-data exposure. 3. For a series, additionally review IDs, DAG, gaps, overlap, terminology, - invariants, handoffs, future scope, and authority. + invariants, handoffs, future scope, and authority. Use schema 1.1, declare + every root, order every target exactly once, and bind the request path and + normalized hash in the result. 4. For a campaign, prove every `featureInput`, one semantic review per unique intake, every worker applicability row, repository/language/stack fit, manifest DAG/base/handoff agreement, and separately governed operator diff --git a/.claude/skills/speckit-intake-review-status/SKILL.md b/.claude/skills/speckit-intake-review-status/SKILL.md index 9386d5b..c271fe0 100644 --- a/.claude/skills/speckit-intake-review-status/SKILL.md +++ b/.claude/skills/speckit-intake-review-status/SKILL.md @@ -21,8 +21,9 @@ invoke the installed validator with the repository root, and report: - target paths, normalized hashes, optional Git blobs, and drift; - findings, questions, accepted-risk ownership, and re-evaluation triggers; - series and campaign coverage, including unique intake and worker counts; +- for Series mode, request hash, root set, target order, edge count, and DAG validity; - whether Preset 7 or Preset 8 may consume the result under current policy; - the exact next command. Do not rewrite timestamps, hashes, status, reports, target files, Git state, or -authority. A successful status check grants no implementation or remote right. \ No newline at end of file +authority. A successful status check grants no implementation or remote right. diff --git a/.claude/skills/speckit-intake-review/SKILL.md b/.claude/skills/speckit-intake-review/SKILL.md index 26aec46..8020196 100644 --- a/.claude/skills/speckit-intake-review/SKILL.md +++ b/.claude/skills/speckit-intake-review/SKILL.md @@ -25,7 +25,9 @@ exist. Use the installed policy and checklist. evidence, delivery authority, risks, references, prompt alignment, and secret or unnecessary-personal-data exposure. 3. For a series, additionally review IDs, DAG, gaps, overlap, terminology, - invariants, handoffs, future scope, and authority. + invariants, handoffs, future scope, and authority. Use schema 1.1, declare + every root, order every target exactly once, and bind the request path and + normalized hash in the result. 4. For a campaign, prove every `featureInput`, one semantic review per unique intake, every worker applicability row, repository/language/stack fit, manifest DAG/base/handoff agreement, and separately governed operator @@ -46,4 +48,4 @@ exist. Use the installed policy and checklist. Finish with outcome, target and worker counts, findings by severity, accepted risks, open questions, result path, and exact next action. Do not start Specify -or an autonomous run implicitly. \ No newline at end of file +or an autonomous run implicitly. diff --git a/.github/agents/speckit.intake-review-status.agent.md b/.github/agents/speckit.intake-review-status.agent.md index 00c150d..24ab544 100644 --- a/.github/agents/speckit.intake-review-status.agent.md +++ b/.github/agents/speckit.intake-review-status.agent.md @@ -18,8 +18,9 @@ invoke the installed validator with the repository root, and report: - target paths, normalized hashes, optional Git blobs, and drift; - findings, questions, accepted-risk ownership, and re-evaluation triggers; - series and campaign coverage, including unique intake and worker counts; +- for Series mode, request hash, root set, target order, edge count, and DAG validity; - whether Preset 7 or Preset 8 may consume the result under current policy; - the exact next command. Do not rewrite timestamps, hashes, status, reports, target files, Git state, or -authority. A successful status check grants no implementation or remote right. \ No newline at end of file +authority. A successful status check grants no implementation or remote right. diff --git a/.github/agents/speckit.intake-review.agent.md b/.github/agents/speckit.intake-review.agent.md index f87b100..30a362c 100644 --- a/.github/agents/speckit.intake-review.agent.md +++ b/.github/agents/speckit.intake-review.agent.md @@ -22,7 +22,9 @@ exist. Use the installed policy and checklist. evidence, delivery authority, risks, references, prompt alignment, and secret or unnecessary-personal-data exposure. 3. For a series, additionally review IDs, DAG, gaps, overlap, terminology, - invariants, handoffs, future scope, and authority. + invariants, handoffs, future scope, and authority. Use schema 1.1, declare + every root, order every target exactly once, and bind the request path and + normalized hash in the result. 4. For a campaign, prove every `featureInput`, one semantic review per unique intake, every worker applicability row, repository/language/stack fit, manifest DAG/base/handoff agreement, and separately governed operator @@ -43,4 +45,4 @@ exist. Use the installed policy and checklist. Finish with outcome, target and worker counts, findings by severity, accepted risks, open questions, result path, and exact next action. Do not start Specify -or an autonomous run implicitly. \ No newline at end of file +or an autonomous run implicitly. diff --git a/.opencode/command/speckit.intake-review-status.md b/.opencode/command/speckit.intake-review-status.md index 00c150d..24ab544 100644 --- a/.opencode/command/speckit.intake-review-status.md +++ b/.opencode/command/speckit.intake-review-status.md @@ -18,8 +18,9 @@ invoke the installed validator with the repository root, and report: - target paths, normalized hashes, optional Git blobs, and drift; - findings, questions, accepted-risk ownership, and re-evaluation triggers; - series and campaign coverage, including unique intake and worker counts; +- for Series mode, request hash, root set, target order, edge count, and DAG validity; - whether Preset 7 or Preset 8 may consume the result under current policy; - the exact next command. Do not rewrite timestamps, hashes, status, reports, target files, Git state, or -authority. A successful status check grants no implementation or remote right. \ No newline at end of file +authority. A successful status check grants no implementation or remote right. diff --git a/.opencode/command/speckit.intake-review.md b/.opencode/command/speckit.intake-review.md index f87b100..30a362c 100644 --- a/.opencode/command/speckit.intake-review.md +++ b/.opencode/command/speckit.intake-review.md @@ -22,7 +22,9 @@ exist. Use the installed policy and checklist. evidence, delivery authority, risks, references, prompt alignment, and secret or unnecessary-personal-data exposure. 3. For a series, additionally review IDs, DAG, gaps, overlap, terminology, - invariants, handoffs, future scope, and authority. + invariants, handoffs, future scope, and authority. Use schema 1.1, declare + every root, order every target exactly once, and bind the request path and + normalized hash in the result. 4. For a campaign, prove every `featureInput`, one semantic review per unique intake, every worker applicability row, repository/language/stack fit, manifest DAG/base/handoff agreement, and separately governed operator @@ -43,4 +45,4 @@ exist. Use the installed policy and checklist. Finish with outcome, target and worker counts, findings by severity, accepted risks, open questions, result path, and exact next action. Do not start Specify -or an autonomous run implicitly. \ No newline at end of file +or an autonomous run implicitly. diff --git a/.specify/presets/.registry b/.specify/presets/.registry index c923f9f..40e6e29 100644 --- a/.specify/presets/.registry +++ b/.specify/presets/.registry @@ -271,51 +271,6 @@ ], "installed_at": "2026-07-21T19:12:26.842905+00:00" }, - "intake-review-governance": { - "version": "0.1.0", - "source": "local", - "manifest_hash": "sha256:2f3d325882dff24249cf0160c1b7dfa98d1053ac2ad10c7ca6045a107d8138ed", - "enabled": true, - "priority": 65, - "registered_commands": { - "agy": [ - "speckit.intake-review", - "speckit.intake-repair", - "speckit.intake-review-status" - ], - "claude": [ - "speckit.intake-review", - "speckit.intake-repair", - "speckit.intake-review-status" - ], - "codex": [ - "speckit.intake-review", - "speckit.intake-repair", - "speckit.intake-review-status" - ], - "copilot": [ - "speckit.intake-review", - "speckit.intake-repair", - "speckit.intake-review-status" - ], - "opencode": [ - "speckit.intake-review", - "speckit.intake-repair", - "speckit.intake-review-status" - ], - "zed": [ - "speckit.intake-review", - "speckit.intake-repair", - "speckit.intake-review-status" - ] - }, - "registered_skills": [ - "speckit-intake-review", - "speckit-intake-repair", - "speckit-intake-review-status" - ], - "installed_at": "2026-07-21T19:12:28.206913+00:00" - }, "autonomous-run-governance": { "version": "0.3.2", "source": "local", @@ -471,6 +426,51 @@ "speckit-intake-create-status" ], "installed_at": "2026-07-23T13:16:33.535306+00:00" + }, + "intake-review-governance": { + "version": "0.1.1", + "source": "local", + "manifest_hash": "sha256:b85a75202be58d1f17b0450124beb5a1f941dfab6828e436ed0e382c8fcb7cd2", + "enabled": true, + "priority": 65, + "registered_commands": { + "agy": [ + "speckit.intake-review", + "speckit.intake-repair", + "speckit.intake-review-status" + ], + "claude": [ + "speckit.intake-review", + "speckit.intake-repair", + "speckit.intake-review-status" + ], + "codex": [ + "speckit.intake-review", + "speckit.intake-repair", + "speckit.intake-review-status" + ], + "copilot": [ + "speckit.intake-review", + "speckit.intake-repair", + "speckit.intake-review-status" + ], + "opencode": [ + "speckit.intake-review", + "speckit.intake-repair", + "speckit.intake-review-status" + ], + "zed": [ + "speckit.intake-review", + "speckit.intake-repair", + "speckit.intake-review-status" + ] + }, + "registered_skills": [ + "speckit-intake-review", + "speckit-intake-repair", + "speckit-intake-review-status" + ], + "installed_at": "2026-07-23T17:44:34.310112+00:00" } } } \ No newline at end of file diff --git a/.specify/presets/intake-review-governance/README.md b/.specify/presets/intake-review-governance/README.md index f1649b3..e99bb91 100644 --- a/.specify/presets/intake-review-governance/README.md +++ b/.specify/presets/intake-review-governance/README.md @@ -1,8 +1,11 @@ # Intake Review Governance Preset Optional, stackable intake-quality governance for GitHub Spec Kit. Version -`0.1.0` adds three commands, eight templates, and read-only Bash/PowerShell -validators. Recommended priority: `65`, between Agent Parity (`60`) and +`0.1.1` provides three commands, eight templates, and read-only +Bash/PowerShell validators. Series reviews use a schema-1.1 request binding +with normalized SHA-256, explicit roots, complete target ordering, and +validated acyclic dependency edges. Single and Campaign schema 1.0 results +remain compatible. Recommended priority: `65`, between Agent Parity (`60`) and Autonomous Run Governance (`70`). Spec Kit `>=0.8.3` is required. ## Commands @@ -16,7 +19,7 @@ Autonomous Run Governance (`70`). Spec Kit `>=0.8.3` is required. ## Install ```bash -specify preset add --from https://github.com/hindermath/spec-kit-preset-intake-review-governance/archive/refs/tags/v0.1.0.zip --priority 65 +specify preset add --from https://github.com/hindermath/spec-kit-preset-intake-review-governance/archive/refs/tags/v0.1.1.zip --priority 65 specify preset list specify preset resolve ``` @@ -30,3 +33,8 @@ activate it through their schema-1.2 `intakeReview` object. Review and status never modify intake targets. Repair needs explicit mutation authority. An agent cannot accept residual risk or invent an operator exception. The validators are read-only and grant no delivery authority. + +An accepted Series result must use schema 1.1 and bind its repository-relative +request through `requestEvidence`. The validators reject request drift, +identity or role mismatch, incomplete ordering, unknown or duplicate edges, +cycles, and roots that differ from the graph's zero-indegree targets. diff --git a/.specify/presets/intake-review-governance/commands/speckit.intake-review-status.md b/.specify/presets/intake-review-governance/commands/speckit.intake-review-status.md index a191c22..2e8da39 100644 --- a/.specify/presets/intake-review-governance/commands/speckit.intake-review-status.md +++ b/.specify/presets/intake-review-governance/commands/speckit.intake-review-status.md @@ -15,6 +15,7 @@ invoke the installed validator with the repository root, and report: - target paths, normalized hashes, optional Git blobs, and drift; - findings, questions, accepted-risk ownership, and re-evaluation triggers; - series and campaign coverage, including unique intake and worker counts; +- for Series mode, request hash, root set, target order, edge count, and DAG validity; - whether Preset 7 or Preset 8 may consume the result under current policy; - the exact next command. diff --git a/.specify/presets/intake-review-governance/commands/speckit.intake-review.md b/.specify/presets/intake-review-governance/commands/speckit.intake-review.md index 0ac0215..b9b73f6 100644 --- a/.specify/presets/intake-review-governance/commands/speckit.intake-review.md +++ b/.specify/presets/intake-review-governance/commands/speckit.intake-review.md @@ -20,7 +20,9 @@ exist. Use the installed policy and checklist. evidence, delivery authority, risks, references, prompt alignment, and secret or unnecessary-personal-data exposure. 3. For a series, additionally review IDs, DAG, gaps, overlap, terminology, - invariants, handoffs, future scope, and authority. + invariants, handoffs, future scope, and authority. Use schema 1.1, declare + every root, order every target exactly once, and bind the request path and + normalized hash in the result. 4. For a campaign, prove every `featureInput`, one semantic review per unique intake, every worker applicability row, repository/language/stack fit, manifest DAG/base/handoff agreement, and separately governed operator diff --git a/.specify/presets/intake-review-governance/docs/README.md b/.specify/presets/intake-review-governance/docs/README.md index 1ccc241..e213afc 100644 --- a/.specify/presets/intake-review-governance/docs/README.md +++ b/.specify/presets/intake-review-governance/docs/README.md @@ -4,3 +4,7 @@ The preset README provides installation and command examples. The installed runbook template defines modes, outcomes, hash normalization, repair limits, risk ownership, and Preset 7/8 consumer gates. The checklist is the normative review-dimension inventory. + +Version 0.1.1 adds the binding Series graph contract: schema-1.1 request +evidence, explicit roots, exact target ordering, edge integrity, and cycle +detection with Bash/PowerShell parity. diff --git a/.specify/presets/intake-review-governance/docs/field-validation-summary.md b/.specify/presets/intake-review-governance/docs/field-validation-summary.md index 7e36548..444739c 100644 --- a/.specify/presets/intake-review-governance/docs/field-validation-summary.md +++ b/.specify/presets/intake-review-governance/docs/field-validation-summary.md @@ -1,6 +1,6 @@ -# Field Validation Summary: v0.1.0 +# Field Validation Summary: v0.1.1 -Date: 2026-07-21 +Date: 2026-07-23 ## Synthetic validation @@ -11,6 +11,34 @@ Date: 2026-07-21 - Missing worker coverage, result drift, expired exceptions, Critical/High findings, and unanswered material questions are blocking cases. +## Series request graph + +A three-target schema-1.1 fixture binds its repository-relative request with +the same strict UTF-8 normalization used for intake targets. Bash and +PowerShell accepted one root and two ordered predecessor edges with identical +exit behavior. The fixture suite rejects missing request evidence, request +hash drift, identity or role mismatch, target-set drift, duplicate or missing +order entries, unknown and self edges, duplicate edges, order contradictions, +cycles, and root sets that differ from the graph's zero-indegree targets. + +The field observation from `SecureServiceHarvester-CSharp` contained 66 +targets and 64 predecessor edges. An independent graph audit found an +intermediate request error that v0.1.0 could not reject. Version 0.1.1 makes +that class of accepted Series evidence impossible without a current, +structurally valid request binding. + +## Package composition and agent parity + +A temporary Spec Kit 0.12.11 project accepted all ten presets at priorities +10 through 80. `add`, `list`, `info`, `resolve`, `disable`, `enable`, `remove`, +and reinstall completed successfully with Intake Review v0.1.1 at priority 65. + +Separate temporary Codex, Claude, Copilot Skills, and Antigravity projects each +contained exactly one generated surface for `speckit.intake-review`, +`speckit.intake-review-status`, and `speckit.intake-repair`. Codex and +Antigravity were intentionally validated in separate projects because both +integrations own the `.agents/skills` layout. + ## TuiVision intake `Lastenheft_15_Post-Wave6-Example-Portfolio-Conformance-Audit.md` was reviewed @@ -42,6 +70,7 @@ row failed before worker scheduling. ## Disposition The tested behavior is provider-neutral and supports publication as -`intake-review-governance` v0.1.0, `autonomous-run-governance` v0.3.2, and +`intake-review-governance` v0.1.1, `autonomous-run-governance` v0.3.2, and `parallel-autonomous-run-governance` v0.2.3. The standard eight-preset profile -remains unchanged; the nine-preset profile is explicit and optional. +remains unchanged; the optional authoring-and-review profile contains ten +presets. diff --git a/.specify/presets/intake-review-governance/docs/man/validate-intake-review-result.1 b/.specify/presets/intake-review-governance/docs/man/validate-intake-review-result.1 index 5a274c2..1f88e4d 100644 --- a/.specify/presets/intake-review-governance/docs/man/validate-intake-review-result.1 +++ b/.specify/presets/intake-review-governance/docs/man/validate-intake-review-result.1 @@ -1,4 +1,4 @@ -.TH VALIDATE-INTAKE-REVIEW-RESULT 1 "July 2026" "intake-review-governance 0.1.0" +.TH VALIDATE-INTAKE-REVIEW-RESULT 1 "July 2026" "intake-review-governance 0.1.1" .SH NAME validate-intake-review-result \- validate a current intake review result .SH SYNOPSIS @@ -7,5 +7,10 @@ validate-intake-review-result \- validate a current intake review result Validates schema, outcome, coverage, risk ownership, and normalized SHA-256 hashes without modifying the repository. Text normalization removes one UTF-8 BOM and converts CRLF or CR to LF; it performs no trimming. +.PP +Series schema 1.1 results bind their request through requestEvidence. The +validator checks request identity, target and role parity, exact ordering, +roots, edge references, duplicate edges, order consistency, and acyclicity. +Single and Campaign schema 1.0 results remain supported. .SH EXIT STATUS Returns 0 for a valid current result and 2 for invalid input or drift. diff --git a/.specify/presets/intake-review-governance/preset.yml b/.specify/presets/intake-review-governance/preset.yml index a696e5f..fe91d3e 100644 --- a/.specify/presets/intake-review-governance/preset.yml +++ b/.specify/presets/intake-review-governance/preset.yml @@ -3,7 +3,7 @@ schema_version: "1.0" preset: id: "intake-review-governance" name: "Intake Review Governance" - version: "0.1.0" + version: "0.1.1" description: "Reviews single, series, and campaign intake files before Spec Kit execution and binds accepted outcomes to normalized content hashes." author: "Thorsten Hindermann" repository: "https://github.com/hindermath/spec-kit-preset-intake-review-governance" diff --git a/.specify/presets/intake-review-governance/scripts/validate-intake-review-result.ps1 b/.specify/presets/intake-review-governance/scripts/validate-intake-review-result.ps1 index 39d6800..85fb847 100644 --- a/.specify/presets/intake-review-governance/scripts/validate-intake-review-result.ps1 +++ b/.specify/presets/intake-review-governance/scripts/validate-intake-review-result.ps1 @@ -44,29 +44,39 @@ function Test-IntakeReviewResult { } return $Value.Trim() } + function Add-GraphError([string]$Code, [string]$Message) { + $Errors.Add("${Code}: $Message") + } function Test-Relative([string]$Value) { if ([IO.Path]::IsPathRooted($Value)) { return $false } return -not ($Value -split '[\\/]' | Where-Object { $_ -eq '..' }) } - function Get-NormalizedHash([string]$Path) { + function Get-NormalizedBytes([string]$Path) { $Bytes = [IO.File]::ReadAllBytes($Path) $Offset = if ($Bytes.Length -ge 3 -and $Bytes[0] -eq 0xEF -and $Bytes[1] -eq 0xBB -and $Bytes[2] -eq 0xBF) { 3 } else { 0 } $Utf8 = [Text.UTF8Encoding]::new($false, $true) $Text = $Utf8.GetString($Bytes, $Offset, $Bytes.Length - $Offset) if ($Text.Contains([char]0)) { throw 'binary NUL detected' } $Normalized = $Text.Replace("`r`n", "`n").Replace("`r", "`n") - $Hash = [Security.Cryptography.SHA256]::HashData($Utf8.GetBytes($Normalized)) + return $Utf8.GetBytes($Normalized) + } + function Get-NormalizedHash([string]$Path) { + $Hash = [Security.Cryptography.SHA256]::HashData((Get-NormalizedBytes $Path)) return [Convert]::ToHexString($Hash).ToLowerInvariant() } if (-not (Test-Path -LiteralPath $Result -PathType Leaf)) { throw "Result file not found: $Result" } $Data = Get-Content -LiteralPath $Result -Raw -Encoding UTF8 | ConvertFrom-Json - if ((Get-Prop $Data 'schemaVersion') -ne '1.0') { $Errors.Add('schemaVersion must be 1.0') } + $SchemaVersion = Get-Prop $Data 'schemaVersion' + if ($SchemaVersion -notin @('1.0', '1.1')) { $Errors.Add('schemaVersion must be 1.0 or 1.1') } $ReviewId = Get-Text $Data 'reviewId' 'result' $Guid = [Guid]::Empty if (-not [Guid]::TryParse($ReviewId, [ref]$Guid) -or $Guid -eq [Guid]::Empty) { $Errors.Add('reviewId must be a non-zero UUID') } $Mode = Get-Text $Data 'mode' 'result' if ($Mode -notin @('Single', 'Series', 'Campaign')) { $Errors.Add('mode is invalid') } + if ($Mode -eq 'Series' -and $SchemaVersion -ne '1.1') { + Add-GraphError 'IRG001' 'Series result schemaVersion must be 1.1' + } $Status = Get-Text $Data 'status' 'result' $Accepted = @('Ready', 'ReadyWithAcceptedRisks') if ($Status -notin ($Accepted + @('NeedsClarification', 'NeedsRemediation', 'Rejected'))) { $Errors.Add('status is invalid') } @@ -78,12 +88,15 @@ function Test-IntakeReviewResult { $Targets = @(Get-Prop $Data 'targets') if ($Targets.Count -eq 0) { $Errors.Add('targets must be a non-empty array') } $TargetPaths = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal) + $TargetRoles = @{} for ($Index = 0; $Index -lt $Targets.Count; $Index++) { $Target = $Targets[$Index]; $Label = "targets[$Index]" $Path = Get-Text $Target 'path' $Label $Digest = Get-Text $Target 'normalizedSha256' $Label - [void](Get-Text $Target 'role' $Label); [void](Get-Text $Target 'gitBlob' $Label) + $Role = Get-Text $Target 'role' $Label + [void](Get-Text $Target 'gitBlob' $Label) if (-not (Test-Relative $Path) -or -not $TargetPaths.Add($Path)) { $Errors.Add("${Label}.path must be unique and repository-relative") } + $TargetRoles[$Path] = $Role if ($Digest -notmatch '^[0-9a-f]{64}$') { $Errors.Add("${Label}.normalizedSha256 is invalid") } $TargetPath = Join-Path ([IO.Path]::GetFullPath($Repo)) $Path if (-not (Test-Path -LiteralPath $TargetPath -PathType Leaf)) { $Errors.Add("target missing: $Path") } @@ -93,6 +106,208 @@ function Test-IntakeReviewResult { } } + $RequestEvidence = Get-Prop $Data 'requestEvidence' + $RequestBindingRequired = $Mode -eq 'Series' -or $null -ne $RequestEvidence + if ($RequestBindingRequired) { + if ($null -eq $RequestEvidence) { + Add-GraphError 'IRG001' 'requestEvidence must be an object' + } + else { + $RequestPathText = Get-Text $RequestEvidence 'path' 'requestEvidence' + $RequestDigest = Get-Text $RequestEvidence 'normalizedSha256' 'requestEvidence' + if ($RequestPathText -and -not (Test-Relative $RequestPathText)) { + Add-GraphError 'IRG001' 'requestEvidence.path must be repository-relative' + } + if ($RequestDigest -notmatch '^[0-9a-f]{64}$') { + Add-GraphError 'IRG001' 'requestEvidence.normalizedSha256 is invalid' + } + $RequestPath = Join-Path ([IO.Path]::GetFullPath($Repo)) $RequestPathText + $RequestData = $null + $ActualRequestDigest = '' + if (-not (Test-Path -LiteralPath $RequestPath -PathType Leaf)) { + Add-GraphError 'IRG001' "request missing: $RequestPathText" + } + else { + try { + $RequestBytes = Get-NormalizedBytes $RequestPath + $ActualRequestDigest = [Convert]::ToHexString( + [Security.Cryptography.SHA256]::HashData($RequestBytes) + ).ToLowerInvariant() + $RequestText = [Text.UTF8Encoding]::new($false, $true).GetString($RequestBytes) + $RequestData = $RequestText | ConvertFrom-Json + } + catch { + Add-GraphError 'IRG001' "invalid request: $($_.Exception.Message)" + } + if ($ActualRequestDigest -and $ActualRequestDigest -ne $RequestDigest) { + Add-GraphError 'IRG001' 'request hash drift' + } + } + + if ($null -ne $RequestData) { + $RequestSchema = Get-Prop $RequestData 'schemaVersion' + $ExpectedRequestSchemas = if ($Mode -eq 'Series') { @('1.1') } else { @('1.0', '1.1') } + if ($RequestSchema -notin $ExpectedRequestSchemas) { + Add-GraphError 'IRG002' "$Mode request schemaVersion is invalid" + } + foreach ($Field in @('reviewId', 'mode', 'policy')) { + if ((Get-Prop $RequestData $Field) -ne (Get-Prop $Data $Field)) { + Add-GraphError 'IRG002' "request.$Field must match result.$Field" + } + } + + $RequestTargets = @(Get-Prop $RequestData 'targets') + if ($RequestTargets.Count -eq 0) { + Add-GraphError 'IRG003' 'request.targets must be a non-empty array' + } + $RequestPaths = [Collections.Generic.List[string]]::new() + $RequestRoles = @{} + for ($Index = 0; $Index -lt $RequestTargets.Count; $Index++) { + $RequestTarget = $RequestTargets[$Index] + $Label = "request.targets[$Index]" + $RequestTargetPath = Get-Text $RequestTarget 'path' $Label + $RequestRole = Get-Text $RequestTarget 'role' $Label + if (-not $RequestTargetPath -or -not (Test-Relative $RequestTargetPath)) { + Add-GraphError 'IRG003' "${Label}.path must be repository-relative" + } + if ($RequestRoles.ContainsKey($RequestTargetPath)) { + Add-GraphError 'IRG003' "duplicate request target: $RequestTargetPath" + } + $RequestPaths.Add($RequestTargetPath) + $RequestRoles[$RequestTargetPath] = $RequestRole + } + $RequestPathSet = [Collections.Generic.HashSet[string]]::new( + [string[]]$RequestPaths, + [StringComparer]::Ordinal + ) + if ($RequestPaths.Count -ne $TargetPaths.Count -or + $RequestPathSet.Count -ne $TargetPaths.Count -or + @($TargetPaths | Where-Object { -not $RequestPathSet.Contains($_) }).Count) { + Add-GraphError 'IRG003' 'request and result target sets must match exactly' + } + foreach ($TargetPathValue in $TargetPaths) { + if ($RequestRoles.ContainsKey($TargetPathValue) -and + $RequestRoles[$TargetPathValue] -ne $TargetRoles[$TargetPathValue]) { + Add-GraphError 'IRG003' "request and result roles differ: $TargetPathValue" + } + } + + if ($Mode -eq 'Series') { + $SeriesData = Get-Prop $RequestData 'series' + if ($null -eq $SeriesData) { + Add-GraphError 'IRG004' 'request.series must be an object' + } + $Ordered = @(Get-Prop $SeriesData 'orderedTargetPaths') + $OrderedSet = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal) + $OrderIndex = @{} + for ($Index = 0; $Index -lt $Ordered.Count; $Index++) { + $OrderedPath = $Ordered[$Index] + if ($OrderedPath -isnot [string] -or [string]::IsNullOrWhiteSpace($OrderedPath)) { + Add-GraphError 'IRG004' 'series.orderedTargetPaths must contain non-empty strings' + continue + } + if (-not $OrderedSet.Add($OrderedPath)) { + Add-GraphError 'IRG004' 'series.orderedTargetPaths contains duplicates' + } + $OrderIndex[$OrderedPath] = $Index + } + if ($Ordered.Count -ne $TargetPaths.Count -or + $OrderedSet.Count -ne $TargetPaths.Count -or + @($TargetPaths | Where-Object { -not $OrderedSet.Contains($_) }).Count) { + Add-GraphError 'IRG004' 'series.orderedTargetPaths must contain every target exactly once' + } + + $Roots = @(Get-Prop $SeriesData 'roots') + $RootSet = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal) + foreach ($Root in $Roots) { + if ($Root -isnot [string] -or [string]::IsNullOrWhiteSpace($Root)) { + Add-GraphError 'IRG008' 'series.roots must contain non-empty strings' + continue + } + if (-not $RootSet.Add($Root)) { + Add-GraphError 'IRG008' 'series.roots contains duplicates' + } + if (-not $TargetPaths.Contains($Root)) { + Add-GraphError 'IRG008' 'series.roots references an unknown target' + } + } + + $Dependencies = @(Get-Prop $SeriesData 'dependencies') + $EdgePairs = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal) + $Adjacency = @{} + $Indegree = @{} + foreach ($TargetPathValue in $TargetPaths) { + $Adjacency[$TargetPathValue] = [Collections.Generic.List[string]]::new() + $Indegree[$TargetPathValue] = 0 + } + for ($Index = 0; $Index -lt $Dependencies.Count; $Index++) { + $Dependency = $Dependencies[$Index] + $Label = "series.dependencies[$Index]" + $Source = Get-Text $Dependency 'from' $Label + $Destination = Get-Text $Dependency 'to' $Label + [void](Get-Text $Dependency 'kind' $Label) + if (-not $TargetPaths.Contains($Source) -or -not $TargetPaths.Contains($Destination)) { + Add-GraphError 'IRG005' "$Label references an unknown target" + continue + } + if ($Source -eq $Destination) { + Add-GraphError 'IRG005' "$Label must not be a self-edge" + continue + } + $Pair = "$Source`0$Destination" + if (-not $EdgePairs.Add($Pair)) { + Add-GraphError 'IRG006' "duplicate dependency edge: $Source -> $Destination" + continue + } + $Adjacency[$Source].Add($Destination) + $Indegree[$Destination] = [int]$Indegree[$Destination] + 1 + if ($OrderIndex.ContainsKey($Source) -and $OrderIndex.ContainsKey($Destination) -and + [int]$OrderIndex[$Source] -ge [int]$OrderIndex[$Destination]) { + Add-GraphError 'IRG004' "dependency contradicts declared order: $Source -> $Destination" + } + } + + $ActualRoots = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal) + foreach ($TargetPathValue in $TargetPaths) { + if ([int]$Indegree[$TargetPathValue] -eq 0) { + [void]$ActualRoots.Add($TargetPathValue) + } + } + if ($RootSet.Count -ne $ActualRoots.Count -or + @($ActualRoots | Where-Object { -not $RootSet.Contains($_) }).Count) { + Add-GraphError 'IRG008' 'series.roots must equal the zero-indegree target set' + } + if ($TargetPaths.Count -gt 0 -and $RootSet.Count -eq 0) { + Add-GraphError 'IRG008' 'Series requires at least one root' + } + + $Remaining = @{} + $Queue = [Collections.Generic.Queue[string]]::new() + foreach ($TargetPathValue in $TargetPaths) { + $Remaining[$TargetPathValue] = [int]$Indegree[$TargetPathValue] + if ([int]$Remaining[$TargetPathValue] -eq 0) { + $Queue.Enqueue($TargetPathValue) + } + } + $Visited = 0 + while ($Queue.Count -gt 0) { + $Node = $Queue.Dequeue() + $Visited++ + foreach ($Successor in $Adjacency[$Node]) { + $Remaining[$Successor] = [int]$Remaining[$Successor] - 1 + if ([int]$Remaining[$Successor] -eq 0) { + $Queue.Enqueue($Successor) + } + } + } + if ($Visited -ne $TargetPaths.Count) { + Add-GraphError 'IRG007' 'series.dependencies must be acyclic' + } + } + } + } + } + $Counts = @{ Critical = 0; High = 0; Medium = 0; Low = 0 } $FindingIds = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal) $Findings = @(Get-Prop $Data 'findings') diff --git a/.specify/presets/intake-review-governance/scripts/validate-intake-review-result.sh b/.specify/presets/intake-review-governance/scripts/validate-intake-review-result.sh index 1353c42..ad9bff1 100644 --- a/.specify/presets/intake-review-governance/scripts/validate-intake-review-result.sh +++ b/.specify/presets/intake-review-governance/scripts/validate-intake-review-result.sh @@ -27,6 +27,9 @@ result_path = Path(sys.argv[1]) repo = Path(sys.argv[2]).resolve() errors = [] +def graph_error(code, message): + errors.append(f"{code}: {message}") + def required_text(obj, key, label): value = obj.get(key) if isinstance(obj, dict) else None if not isinstance(value, str) or not value.strip(): @@ -58,13 +61,16 @@ except Exception as exc: if not isinstance(data, dict): errors.append("result root must be an object"); data = {} -if data.get("schemaVersion") != "1.0": errors.append("schemaVersion must be 1.0") +schema_version = data.get("schemaVersion") +if schema_version not in ("1.0", "1.1"): errors.append("schemaVersion must be 1.0 or 1.1") review_id = required_text(data, "reviewId", "result") try: if uuid.UUID(review_id).int == 0: errors.append("reviewId must not be the starter UUID") except ValueError: errors.append("reviewId must be a UUID") mode = required_text(data, "mode", "result") if mode not in ("Single", "Series", "Campaign"): errors.append("mode is invalid") +if mode == "Series" and schema_version != "1.1": + graph_error("IRG001", "Series result schemaVersion must be 1.1") status = required_text(data, "status", "result") accepted = ("Ready", "ReadyWithAcceptedRisks") if status not in accepted + ("NeedsClarification", "NeedsRemediation", "Rejected"): @@ -79,15 +85,17 @@ except ValueError: errors.append("reviewedAt must be an ISO-8601 UTC timestamp") targets = data.get("targets") if not isinstance(targets, list) or not targets: errors.append("targets must be a non-empty array"); targets = [] target_paths = set() +target_roles = {} for i, target in enumerate(targets): label = f"targets[{i}]" path_text = required_text(target, "path", label) digest = required_text(target, "normalizedSha256", label) - required_text(target, "role", label) + role = required_text(target, "role", label) required_text(target, "gitBlob", label) if path_text and (not relative(path_text) or path_text in target_paths): errors.append(f"{label}.path must be unique and repository-relative") target_paths.add(path_text) + target_roles[path_text] = role if not re.fullmatch(r"[0-9a-f]{64}", digest): errors.append(f"{label}.normalizedSha256 is invalid") target_path = repo / path_text if not target_path.is_file(): errors.append(f"target missing: {path_text}") @@ -97,6 +105,143 @@ for i, target in enumerate(targets): else: if actual != digest: errors.append(f"target hash drift: {path_text}") +request_evidence = data.get("requestEvidence") +request_binding_required = mode == "Series" or request_evidence is not None +if request_binding_required: + if not isinstance(request_evidence, dict): + graph_error("IRG001", "requestEvidence must be an object") + else: + request_path_text = required_text(request_evidence, "path", "requestEvidence") + request_digest = required_text(request_evidence, "normalizedSha256", "requestEvidence") + if request_path_text and not relative(request_path_text): + graph_error("IRG001", "requestEvidence.path must be repository-relative") + if not re.fullmatch(r"[0-9a-f]{64}", request_digest): + graph_error("IRG001", "requestEvidence.normalizedSha256 is invalid") + request_path = repo / request_path_text + request_data = None + if not request_path.is_file(): + graph_error("IRG001", f"request missing: {request_path_text}") + else: + try: + request_bytes = normalized(request_path) + actual_request_digest = hashlib.sha256(request_bytes).hexdigest() + request_data = json.loads(request_bytes.decode("utf-8")) + except (ValueError, json.JSONDecodeError) as exc: + graph_error("IRG001", f"invalid request: {exc}") + else: + if actual_request_digest != request_digest: + graph_error("IRG001", "request hash drift") + if request_data is not None: + if not isinstance(request_data, dict): + graph_error("IRG002", "request root must be an object") + request_data = {} + request_schema = request_data.get("schemaVersion") + expected_request_schemas = ("1.1",) if mode == "Series" else ("1.0", "1.1") + if request_schema not in expected_request_schemas: + graph_error("IRG002", f"{mode} request schemaVersion is invalid") + for field, expected in (("reviewId", review_id), ("mode", mode), ("policy", data.get("policy"))): + if request_data.get(field) != expected: + graph_error("IRG002", f"request.{field} must match result.{field}") + + request_targets = request_data.get("targets") + if not isinstance(request_targets, list) or not request_targets: + graph_error("IRG003", "request.targets must be a non-empty array") + request_targets = [] + request_paths = [] + request_roles = {} + for i, request_target in enumerate(request_targets): + label = f"request.targets[{i}]" + path_text = required_text(request_target, "path", label) + role = required_text(request_target, "role", label) + if not path_text or not relative(path_text): + graph_error("IRG003", f"{label}.path must be repository-relative") + if path_text in request_roles: + graph_error("IRG003", f"duplicate request target: {path_text}") + request_paths.append(path_text) + request_roles[path_text] = role + if set(request_paths) != target_paths or len(request_paths) != len(target_paths): + graph_error("IRG003", "request and result target sets must match exactly") + for path_text in target_paths & set(request_paths): + if request_roles.get(path_text) != target_roles.get(path_text): + graph_error("IRG003", f"request and result roles differ: {path_text}") + + if mode == "Series": + series_data = request_data.get("series") + if not isinstance(series_data, dict): + graph_error("IRG004", "request.series must be an object") + series_data = {} + ordered = series_data.get("orderedTargetPaths") + if not isinstance(ordered, list): + graph_error("IRG004", "series.orderedTargetPaths must be an array") + ordered = [] + if any(not isinstance(path, str) or not path.strip() for path in ordered): + graph_error("IRG004", "series.orderedTargetPaths must contain non-empty strings") + if len(ordered) != len(set(ordered)): + graph_error("IRG004", "series.orderedTargetPaths contains duplicates") + if set(ordered) != target_paths or len(ordered) != len(target_paths): + graph_error("IRG004", "series.orderedTargetPaths must contain every target exactly once") + order_index = {path: index for index, path in enumerate(ordered)} + + roots = series_data.get("roots") + if not isinstance(roots, list): + graph_error("IRG008", "series.roots must be an array") + roots = [] + if any(not isinstance(root, str) or not root.strip() for root in roots): + graph_error("IRG008", "series.roots must contain non-empty strings") + if len(roots) != len(set(roots)): + graph_error("IRG008", "series.roots contains duplicates") + if any(root not in target_paths for root in roots): + graph_error("IRG008", "series.roots references an unknown target") + + dependencies = series_data.get("dependencies") + if not isinstance(dependencies, list): + graph_error("IRG005", "series.dependencies must be an array") + dependencies = [] + edge_pairs = set() + adjacency = {path: [] for path in target_paths} + indegree = {path: 0 for path in target_paths} + for i, dependency in enumerate(dependencies): + label = f"series.dependencies[{i}]" + source = required_text(dependency, "from", label) + destination = required_text(dependency, "to", label) + required_text(dependency, "kind", label) + if source not in target_paths or destination not in target_paths: + graph_error("IRG005", f"{label} references an unknown target") + continue + if source == destination: + graph_error("IRG005", f"{label} must not be a self-edge") + continue + pair = (source, destination) + if pair in edge_pairs: + graph_error("IRG006", f"duplicate dependency edge: {source} -> {destination}") + continue + edge_pairs.add(pair) + adjacency[source].append(destination) + indegree[destination] += 1 + if source in order_index and destination in order_index and order_index[source] >= order_index[destination]: + graph_error("IRG004", f"dependency contradicts declared order: {source} -> {destination}") + + actual_roots = {path for path, degree in indegree.items() if degree == 0} + if set(roots) != actual_roots: + graph_error("IRG008", "series.roots must equal the zero-indegree target set") + if target_paths and not roots: + graph_error("IRG008", "Series requires at least one root") + if any(path not in set(roots) and indegree[path] == 0 for path in target_paths): + graph_error("IRG008", "every non-root target needs an incoming dependency") + + remaining = dict(indegree) + queue = [path for path, degree in remaining.items() if degree == 0] + visited = 0 + while queue: + node = queue.pop() + visited += 1 + for successor in adjacency[node]: + remaining[successor] -= 1 + if remaining[successor] == 0: + queue.append(successor) + if visited != len(target_paths): + graph_error("IRG007", "series.dependencies must be acyclic") + findings = data.get("findings") if not isinstance(findings, list): errors.append("findings must be an array"); findings = [] finding_ids = set(); severity_counts = {k: 0 for k in ("Critical", "High", "Medium", "Low")} diff --git a/.specify/presets/intake-review-governance/templates/agent-file-addendum.md b/.specify/presets/intake-review-governance/templates/agent-file-addendum.md index b5de531..43176d9 100644 --- a/.specify/presets/intake-review-governance/templates/agent-file-addendum.md +++ b/.specify/presets/intake-review-governance/templates/agent-file-addendum.md @@ -7,3 +7,6 @@ until a current review result matches every normalized intake hash. Only findings, unanswered material questions, stale hashes, and missing worker coverage as blocking. Review commands never modify target intakes. Use the separate repair command only with explicit mutation authority, then re-review. +For Series mode require schema 1.1, a hash-bound repository-relative request, +complete target ordering, explicit roots, and an acyclic dependency graph. +Never infer a missing predecessor or silently accept request drift. diff --git a/.specify/presets/intake-review-governance/templates/field-validation-summary.md b/.specify/presets/intake-review-governance/templates/field-validation-summary.md index b706d07..9d33e64 100644 --- a/.specify/presets/intake-review-governance/templates/field-validation-summary.md +++ b/.specify/presets/intake-review-governance/templates/field-validation-summary.md @@ -5,3 +5,8 @@ project intake review, and immutable campaign evidence. Include target count, unique intake count, worker applicability count, positive and negative cases, hashes, command exits, and residual risks. Publication requires no unresolved Critical or High finding. + +For Series schema 1.1, record request hash binding, roots, target-order +cardinality, edge count, Bash/PowerShell error-class parity, and negative +fixtures for drift, target mismatch, invalid references, duplicate edges, +order contradiction, missing predecessors, and cycles. diff --git a/.specify/presets/intake-review-governance/templates/intake-review-checklist-template.md b/.specify/presets/intake-review-governance/templates/intake-review-checklist-template.md index abd87ae..8e05c01 100644 --- a/.specify/presets/intake-review-governance/templates/intake-review-checklist-template.md +++ b/.specify/presets/intake-review-governance/templates/intake-review-checklist-template.md @@ -13,6 +13,10 @@ ## Series - [ ] IDs, order, dependency graph, handoffs, and future-scope boundaries are consistent. +- [ ] Schema 1.1 binds the repository-relative request path and normalized SHA-256. +- [ ] Every target occurs exactly once in `orderedTargetPaths`. +- [ ] Declared roots equal the graph's zero-indegree targets. +- [ ] Every non-root has an incoming edge; edges are unique, known, ordered, and acyclic. - [ ] No unexplained gap, overlap, duplicate ownership, or terminology drift remains. - [ ] Shared invariants are stated once and consumed consistently. diff --git a/.specify/presets/intake-review-governance/templates/intake-review-request-template.json b/.specify/presets/intake-review-governance/templates/intake-review-request-template.json index b19584d..70f19a2 100644 --- a/.specify/presets/intake-review-governance/templates/intake-review-request-template.json +++ b/.specify/presets/intake-review-governance/templates/intake-review-request-template.json @@ -1,12 +1,12 @@ { - "schemaVersion": "1.0", + "schemaVersion": "1.1", "reviewId": "00000000-0000-0000-0000-000000000000", "mode": "Single", "policy": "generic-markdown", "targets": [ { "path": "path/to/intake.md", "role": "Primary" } ], - "series": { "orderedTargetPaths": [], "dependencies": [] }, + "series": { "orderedTargetPaths": [], "roots": [], "dependencies": [] }, "campaign": { "manifestPath": "N/A", "workers": [], diff --git a/.specify/presets/intake-review-governance/templates/intake-review-result-template.json b/.specify/presets/intake-review-governance/templates/intake-review-result-template.json index ddeae7a..95a0531 100644 --- a/.specify/presets/intake-review-governance/templates/intake-review-result-template.json +++ b/.specify/presets/intake-review-governance/templates/intake-review-result-template.json @@ -1,11 +1,15 @@ { - "schemaVersion": "1.0", + "schemaVersion": "1.1", "reviewId": "00000000-0000-0000-0000-000000000000", "mode": "Single", "status": "NeedsClarification", "policy": "generic-markdown", "reviewedAt": "1970-01-01T00:00:00Z", "repository": { "root": ".", "head": "N/A" }, + "requestEvidence": { + "path": "path/to/intake-review-request.json", + "normalizedSha256": "0000000000000000000000000000000000000000000000000000000000000000" + }, "targets": [ { "path": "path/to/intake.md", diff --git a/.specify/presets/intake-review-governance/templates/intake-review-runbook-template.md b/.specify/presets/intake-review-governance/templates/intake-review-runbook-template.md index 805e9ae..8caebdf 100644 --- a/.specify/presets/intake-review-governance/templates/intake-review-runbook-template.md +++ b/.specify/presets/intake-review-governance/templates/intake-review-runbook-template.md @@ -35,6 +35,20 @@ not trim spaces or final lines. Hash the resulting bytes with SHA-256. Binary or non-UTF-8 input is rejected. This normalization makes equivalent text portable without hiding meaningful edits. +## Series Graph Contract + +Series requests and results use schema 1.1. The result binds the +repository-relative request path and its normalized SHA-256 through +`requestEvidence`. Target paths are the canonical identities. Request and +result review ID, mode, policy, target set, and roles must match. + +`orderedTargetPaths` contains every target exactly once. `roots` equals the +zero-indegree target set. Every non-root has an incoming dependency. Each edge +has `from`, `to`, and `kind`, references known distinct targets, occurs once +per `from/to` pair, follows the declared order, and participates in an acyclic +graph. Never invent a predecessor when repository evidence is ambiguous; +return `NeedsClarification`. + ## Consumer Contract Preset 7 and Preset 8 remain usable without this optional preset. When the diff --git a/.specify/presets/intake-review-governance/tests/test-intake-review-validator.ps1 b/.specify/presets/intake-review-governance/tests/test-intake-review-validator.ps1 index 414f478..1de809e 100644 --- a/.specify/presets/intake-review-governance/tests/test-intake-review-validator.ps1 +++ b/.specify/presets/intake-review-governance/tests/test-intake-review-validator.ps1 @@ -28,6 +28,94 @@ function Invoke-Validator([string]$Kind, [int]$ExpectedExit) { return ($Output -join "`n") } +function Save-Json([object]$Value, [string]$Path) { + $Value | ConvertTo-Json -Depth 30 | Set-Content -LiteralPath $Path -Encoding utf8NoBOM +} + +function Reset-SeriesFixture { + foreach ($Name in @('a', 'b', 'c')) { + [IO.File]::WriteAllText( + (Join-Path $TempRoot "intakes/$Name.md"), + "# $Name`n`nDeliver $Name proof.`n", + [Text.UTF8Encoding]::new($false) + ) + } + $script:SeriesRequest = [ordered]@{ + schemaVersion = '1.1' + reviewId = [Guid]::NewGuid().ToString() + mode = 'Series' + policy = 'series-graph-fixture' + targets = @( + [ordered]@{ path = 'intakes/a.md'; role = 'Primary' } + [ordered]@{ path = 'intakes/b.md'; role = 'OrderedMember' } + [ordered]@{ path = 'intakes/c.md'; role = 'OrderedMember' } + ) + series = [ordered]@{ + orderedTargetPaths = @('intakes/a.md', 'intakes/b.md', 'intakes/c.md') + roots = @('intakes/a.md') + dependencies = @( + [ordered]@{ from = 'intakes/a.md'; to = 'intakes/b.md'; kind = 'SeriesPredecessor' } + [ordered]@{ from = 'intakes/b.md'; to = 'intakes/c.md'; kind = 'SeriesPredecessor' } + ) + } + campaign = [ordered]@{ manifestPath = 'N/A'; workers = @(); operatorExceptions = @() } + } + Save-Json $script:SeriesRequest (Join-Path $TempRoot 'request.json') + $script:SeriesResult = [ordered]@{ + schemaVersion = '1.1' + reviewId = $script:SeriesRequest.reviewId + mode = 'Series' + status = 'Ready' + policy = $script:SeriesRequest.policy + reviewedAt = [DateTime]::UtcNow.ToString('o') + repository = [ordered]@{ root = '.'; head = 'N/A' } + targets = @( + [ordered]@{ path = 'intakes/a.md'; role = 'Primary'; normalizedSha256 = Get-NormalizedHash (Join-Path $TempRoot 'intakes/a.md'); gitBlob = 'N/A' } + [ordered]@{ path = 'intakes/b.md'; role = 'OrderedMember'; normalizedSha256 = Get-NormalizedHash (Join-Path $TempRoot 'intakes/b.md'); gitBlob = 'N/A' } + [ordered]@{ path = 'intakes/c.md'; role = 'OrderedMember'; normalizedSha256 = Get-NormalizedHash (Join-Path $TempRoot 'intakes/c.md'); gitBlob = 'N/A' } + ) + requestEvidence = [ordered]@{ + path = 'request.json' + normalizedSha256 = Get-NormalizedHash (Join-Path $TempRoot 'request.json') + } + findings = @() + questions = @() + acceptedRisks = @() + operatorExceptions = @() + coverage = [ordered]@{ + individual = @('intakes/a.md', 'intakes/b.md', 'intakes/c.md') + series = @('Three targets, one root, two predecessor edges') + workers = @() + } + summary = [ordered]@{ critical = 0; high = 0; medium = 0; low = 0 } + supersedes = 'N/A' + } + Save-Json $script:SeriesResult (Join-Path $TempRoot 'result.json') +} + +function Invoke-SeriesCase { + param( + [Parameter(Mandatory)][scriptblock]$Mutation, + [Parameter(Mandatory)][string]$ExpectedClass, + [switch]$RebindRequest + ) + + Reset-SeriesFixture + & $Mutation $script:SeriesRequest $script:SeriesResult + Save-Json $script:SeriesRequest (Join-Path $TempRoot 'request.json') + if ($RebindRequest) { + $script:SeriesResult.requestEvidence.normalizedSha256 = + Get-NormalizedHash (Join-Path $TempRoot 'request.json') + } + Save-Json $script:SeriesResult (Join-Path $TempRoot 'result.json') + foreach ($Kind in @('Bash', 'PowerShell')) { + $Output = Invoke-Validator $Kind 2 + if ($Output -notmatch [regex]::Escape($ExpectedClass)) { + throw "$Kind did not report $ExpectedClass`: $Output" + } + } +} + try { [IO.File]::WriteAllText((Join-Path $TempRoot 'intakes/a.md'), "# Goal`r`n`r`nDeliver proof.`r`n", [Text.UTF8Encoding]::new($false)) $Hash = Get-NormalizedHash (Join-Path $TempRoot 'intakes/a.md') @@ -61,7 +149,90 @@ try { }) $Result | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath (Join-Path $TempRoot 'result.json') -Encoding utf8NoBOM [void](Invoke-Validator Bash 2); [void](Invoke-Validator PowerShell 2) - Write-Output 'PASS: intake-review validator parity and negative cases' + + Reset-SeriesFixture + $BashSeriesPass = Invoke-Validator Bash 0 + $PowerShellSeriesPass = Invoke-Validator PowerShell 0 + if ($BashSeriesPass -notmatch 'PASS:' -or $PowerShellSeriesPass -notmatch 'PASS:') { + throw 'Series positive output parity failed' + } + + $RequestPath = Join-Path $TempRoot 'request.json' + $LfRequest = [IO.File]::ReadAllText($RequestPath, [Text.UTF8Encoding]::new($false, $true)) + [IO.File]::WriteAllText( + $RequestPath, + ([char]0xFEFF) + $LfRequest.Replace("`n", "`r`n"), + [Text.UTF8Encoding]::new($false) + ) + [void](Invoke-Validator Bash 0) + [void](Invoke-Validator PowerShell 0) + + Invoke-SeriesCase -ExpectedClass IRG001 -Mutation { + param($Request, $Result) + $Result.Remove('requestEvidence') + } + Invoke-SeriesCase -ExpectedClass IRG001 -Mutation { + param($Request, $Result) + $Request.series.roots = @('intakes/a.md', 'intakes/c.md') + } + Invoke-SeriesCase -ExpectedClass IRG002 -RebindRequest -Mutation { + param($Request, $Result) + $Request.policy = 'wrong-policy' + } + Invoke-SeriesCase -ExpectedClass IRG003 -RebindRequest -Mutation { + param($Request, $Result) + $Result.targets[1].role = 'WrongRole' + } + Invoke-SeriesCase -ExpectedClass IRG003 -RebindRequest -Mutation { + param($Request, $Result) + $Request.targets = @($Request.targets | Select-Object -First 2) + } + Invoke-SeriesCase -ExpectedClass IRG004 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.orderedTargetPaths = @('intakes/a.md', 'intakes/b.md', 'intakes/b.md') + } + Invoke-SeriesCase -ExpectedClass IRG005 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.dependencies[1].to = 'intakes/missing.md' + } + Invoke-SeriesCase -ExpectedClass IRG005 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.dependencies[1].to = 'intakes/b.md' + } + Invoke-SeriesCase -ExpectedClass IRG006 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.dependencies += [ordered]@{ + from = 'intakes/a.md'; to = 'intakes/b.md'; kind = 'DuplicateKind' + } + } + Invoke-SeriesCase -ExpectedClass IRG007 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.dependencies += [ordered]@{ + from = 'intakes/c.md'; to = 'intakes/a.md'; kind = 'Cycle' + } + $Request.series.roots = @() + } + Invoke-SeriesCase -ExpectedClass IRG004 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.dependencies[0] = [ordered]@{ + from = 'intakes/b.md'; to = 'intakes/a.md'; kind = 'WrongOrder' + } + $Request.series.roots = @('intakes/b.md') + } + Invoke-SeriesCase -ExpectedClass IRG008 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.dependencies = @($Request.series.dependencies | Select-Object -First 1) + } + Invoke-SeriesCase -ExpectedClass IRG008 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.roots = @('intakes/a.md', 'intakes/a.md') + } + Invoke-SeriesCase -ExpectedClass IRG008 -RebindRequest -Mutation { + param($Request, $Result) + $Request.series.roots = @('intakes/a.md', 'intakes/b.md') + } + + Write-Output 'PASS: intake-review validator parity, Series 1.1 graph contract, and negative cases' } finally { Remove-Item -LiteralPath $TempRoot -Recurse -Force -ErrorAction SilentlyContinue } diff --git a/scripts/config/spec-kit-intake-authoring-governance-presets.json b/scripts/config/spec-kit-intake-authoring-governance-presets.json index 12c5588..7d2bfe9 100644 --- a/scripts/config/spec-kit-intake-authoring-governance-presets.json +++ b/scripts/config/spec-kit-intake-authoring-governance-presets.json @@ -9,7 +9,7 @@ {"id":"cross-platform-governance","version":"v0.2.1","priority":50,"repository":"https://github.com/hindermath/spec-kit-preset-cross-platform-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-cross-platform-governance/archive/refs/tags/v0.2.1.zip"}, {"id":"agent-parity-governance","version":"v0.4.0","priority":60,"repository":"https://github.com/hindermath/spec-kit-preset-agent-parity-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-agent-parity-governance/archive/refs/tags/v0.4.0.zip"}, {"id":"intake-authoring-governance","version":"v0.1.1","priority":64,"repository":"https://github.com/hindermath/spec-kit-preset-intake-authoring-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-intake-authoring-governance/archive/refs/tags/v0.1.1.zip"}, - {"id":"intake-review-governance","version":"v0.1.0","priority":65,"repository":"https://github.com/hindermath/spec-kit-preset-intake-review-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-intake-review-governance/archive/refs/tags/v0.1.0.zip"}, + {"id":"intake-review-governance","version":"v0.1.1","priority":65,"repository":"https://github.com/hindermath/spec-kit-preset-intake-review-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-intake-review-governance/archive/refs/tags/v0.1.1.zip"}, {"id":"autonomous-run-governance","version":"v0.3.2","priority":70,"repository":"https://github.com/hindermath/spec-kit-preset-autonomous-run-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-autonomous-run-governance/archive/refs/tags/v0.3.2.zip"}, {"id":"parallel-autonomous-run-governance","version":"v0.2.3","priority":80,"repository":"https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance/archive/refs/tags/v0.2.3.zip"} ] diff --git a/scripts/config/spec-kit-intake-review-governance-presets.json b/scripts/config/spec-kit-intake-review-governance-presets.json index 2e4eae3..79ee6a4 100644 --- a/scripts/config/spec-kit-intake-review-governance-presets.json +++ b/scripts/config/spec-kit-intake-review-governance-presets.json @@ -8,7 +8,7 @@ {"id":"a11y-governance","version":"v0.4.1","priority":40,"repository":"https://github.com/hindermath/spec-kit-preset-a11y-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-a11y-governance/archive/refs/tags/v0.4.1.zip"}, {"id":"cross-platform-governance","version":"v0.2.1","priority":50,"repository":"https://github.com/hindermath/spec-kit-preset-cross-platform-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-cross-platform-governance/archive/refs/tags/v0.2.1.zip"}, {"id":"agent-parity-governance","version":"v0.4.0","priority":60,"repository":"https://github.com/hindermath/spec-kit-preset-agent-parity-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-agent-parity-governance/archive/refs/tags/v0.4.0.zip"}, - {"id":"intake-review-governance","version":"v0.1.0","priority":65,"repository":"https://github.com/hindermath/spec-kit-preset-intake-review-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-intake-review-governance/archive/refs/tags/v0.1.0.zip"}, + {"id":"intake-review-governance","version":"v0.1.1","priority":65,"repository":"https://github.com/hindermath/spec-kit-preset-intake-review-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-intake-review-governance/archive/refs/tags/v0.1.1.zip"}, {"id":"autonomous-run-governance","version":"v0.3.2","priority":70,"repository":"https://github.com/hindermath/spec-kit-preset-autonomous-run-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-autonomous-run-governance/archive/refs/tags/v0.3.2.zip"}, {"id":"parallel-autonomous-run-governance","version":"v0.2.3","priority":80,"repository":"https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance","archiveUrl":"https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance/archive/refs/tags/v0.2.3.zip"} ] diff --git a/scripts/templates/AGENTS.md.tmpl b/scripts/templates/AGENTS.md.tmpl index 749cf80..d236f7f 100644 --- a/scripts/templates/AGENTS.md.tmpl +++ b/scripts/templates/AGENTS.md.tmpl @@ -210,9 +210,9 @@ Neue Lernreihen fuer Fachinformatiker*innen und weitere IT-Ausbildungsberufe wer ## Spec-Kit-Presets / Spec Kit Presets - Standard-Preset-Set: `security-governance` v0.6.1 prio 10, `architecture-governance` v0.5.1 prio 20, `isaqb-architecture-governance` v0.2.1 prio 30, `a11y-governance` v0.4.1 prio 40, `cross-platform-governance` v0.2.1 prio 50, `agent-parity-governance` v0.4.0 prio 60, `autonomous-run-governance` v0.3.2 prio 70, `parallel-autonomous-run-governance` v0.2.3 prio 80. -- Optionale Intake-Presets: `intake-authoring-governance` v0.1.0 prio 64 und `intake-review-governance` v0.1.0 prio 65 bleiben ausserhalb der Standard-Achtermatrix. Die verwaltete Thorsten-Flotte waehlt beide ueber `intake-authoring-ten-governance-presets`; das bisherige Neunerprofil bleibt kompatibel verfuegbar. Neue registrierte Flotten-Repositories erben `defaultPresetProfile`. *Optional intake presets: Authoring v0.1.0 at priority 64 and Review v0.1.0 at priority 65 remain outside the standard eight. Thorsten's managed fleet selects both through the explicit ten-preset profile; the prior nine-preset profile remains available.* +- Optionale Intake-Presets: `intake-authoring-governance` v0.1.1 prio 64 und `intake-review-governance` v0.1.1 prio 65 bleiben ausserhalb der Standard-Achtermatrix. Die verwaltete Thorsten-Flotte waehlt beide ueber `intake-authoring-ten-governance-presets`; das bisherige Neunerprofil bleibt kompatibel verfuegbar. Neue registrierte Flotten-Repositories erben `defaultPresetProfile`. *Optional intake presets: Authoring v0.1.1 at priority 64 and Review v0.1.1 at priority 65 remain outside the standard eight. Thorsten's managed fleet selects both through the explicit ten-preset profile; the prior nine-preset profile remains available.* - Intake Authoring erzeugt aus ausdruecklich benannten, geordneten UTF-8-Quellen genau einen Intake und ein hashgebundenes Receipt. Materielle Fragen werden einzeln und hoechstens fuenfmal pro Durchgang gestellt; offene Fragen erzeugen `NeedsClarification` mit `BLOCKED - DO NOT RUN`. Bestehende Ziele benoetigen ausdrueckliche Update-Autoritaet. `ReadyForReview` ist keine Review-Freigabe. Der naechste Schritt wird als `speckit.intake-review` gemeldet, aber nie automatisch gestartet. Ohne ausdrueckliche Remote-Autoritaet verwendet der erzeugte Autonomous-Prompt `LocalImplementation`. Lernreihen duerfen Intakes vorbereiten und paketieren, starten aber keinen Lernendenlauf ohne ausdruecklichen Auftrag. *Intake Authoring creates one intake and hash-bound receipt from explicitly named ordered UTF-8 sources. It never starts review or execution, never infers overwrite or remote authority, and learner runs still require explicit authorization.* -- Intake Review akzeptiert bei aktiver Projekt- oder Kampagnenpolicy nur aktuelle `Ready`- oder menschlich akzeptierte `ReadyWithAcceptedRisks`-Ergebnisse; Critical/High, offene materielle Fragen, Hash-Drift oder fehlende Worker-Coverage blockieren. Review und Status sind read-only, Repair benoetigt ausdrueckliche Aenderungsautoritaet. +- Intake Review akzeptiert bei aktiver Projekt- oder Kampagnenpolicy nur aktuelle `Ready`- oder menschlich akzeptierte `ReadyWithAcceptedRisks`-Ergebnisse; Critical/High, offene materielle Fragen, Hash-Drift oder fehlende Worker-Coverage blockieren. Review und Status sind read-only, Repair benoetigt ausdrueckliche Aenderungsautoritaet. Series-Reviews verwenden Schema 1.1, binden den normalisierten Request-Hash und pruefen exakte Zielreihenfolge, explizite Roots sowie einen azyklischen Graphen; nicht belegbare Vorgaengerbeziehungen fuehren zu `NeedsClarification`. *Series reviews use schema 1.1, bind the normalized request hash, and verify exact target order, explicit roots, and an acyclic graph; unprovable predecessor relations result in `NeedsClarification`.* - `autonomous-run-governance` v0.3.2 prio 70 ist Teil der Standard-Achtermatrix. Ein vollständiger autonomer Lauf bleibt ausdrücklich delegationspflichtig; die Installation allein erteilt weder Ausführungsberechtigung noch Remote-, Merge-, Bypass- oder Provider-Rechte und `LocalImplementation` bleibt Default. Dokumentations-, Status-, Schema- oder Evidence-Änderungen gelten erst dann als testfrei, wenn keine ausführbaren Validatoren die geänderten Pfade, Marker, Schemas oder Zustandswerte konsumieren. Vor autorisierten Commits wird der exakt beabsichtigte Kandidat mit `git diff --cached --check` und Statusabgleich geprüft; fremde Änderungen bleiben unberührt. Vor einem Merge wird jeder Acceptance-Gate dem tatsächlich ausgeführten Workflow, Job, Runner beziehungsweise der Plattform und dem Befehl zugeordnet; grüne Namen oder ein Bypass ersetzen keinen technischen Nachweis. Bewusst pausierte Läufe werden als `PausedByUser` gespeichert und nur über `speckit.autonomous-resume` fortgesetzt; `speckit.autonomous-stop` wirkt kooperativ am nächsten sicheren Grenzpunkt, und ein gespeicherter Delivery-Modus ist keine aktuelle Berechtigung. Nach Preset- oder Governance-Drift werden neue zwingende Korrektheits-, Sicherheits-, Berechtigungs- und Evidenzregeln minimal mit akzeptierten Plan-, Task- und Checklist-Artefakten abgeglichen; reine Effizienzpräferenzen lösen keine rückwirkende Neugenerierung aus. Die lesbare Skill-Überschrift `Deliver` ist kein Run-State-Wert; für Remote-Closeout gelten ausschließlich `Publish`, `Review` oder `MergeAndSync`. - `parallel-autonomous-run-governance` v0.2.3 prio 80 ist Teil der Standard-Achtermatrix. Die Installation startet keine Kampagne und erteilt keine zusaetzlichen Remote-, Merge-, Bypass-, Abbruch-, Secret- oder Provider-Rechte. Kampagnen bleiben ausdruecklich delegationspflichtig, verwenden getrennte Worktrees und maximal drei gleichzeitig aktive Worker. Schema 1.1 erlaubt ein `runnerProfile` je Worker mit Kampagnen-Fallback; Modell und Reasoning-Stufe sind optionale, nicht geheime Metadaten und werden ohne Deklaration nicht erraten. Konsolidierung verlangt exakten Head, aktuelle Review- und Check-Evidenz, ist nach Teilmerges fortsetzbar und setzt `Completed` erst nach Synchronisation, manifestdeklarierten idempotenten Post-Merge-Aktionen und Abschlussvalidierung. - Reale Preset-8-Kampagnen setzen in jedem Worker-Repository ein installiertes und aktiviertes `autonomous-run-governance >=0.2.2` voraus. Preset 7 mit Prioritaet `70` liefert Lebenszyklus, Evidenz und Berechtigungsgrenzen; Preset 8 mit Prioritaet `80` koordiniert die Kampagne. Fehlt Preset 7, ist es deaktiviert oder zu alt, endet der Preflight vor dem Worker-Start. `requireAutonomousPreset: false` bleibt auf isolierte interne Fixtures begrenzt und ist kein Produktionsmodus. *Real Preset 8 campaigns require installed and enabled `autonomous-run-governance >=0.2.2` in every worker repository. Preset 7 at priority `70` supplies lifecycle, evidence, and authority boundaries; Preset 8 at priority `80` coordinates the campaign. Missing, disabled, or outdated Preset 7 fails preflight before worker start. `requireAutonomousPreset: false` remains limited to isolated internal fixtures and is not a production mode.* diff --git a/scripts/templates/CLAUDE.md.tmpl b/scripts/templates/CLAUDE.md.tmpl index e30d267..c56f16f 100644 --- a/scripts/templates/CLAUDE.md.tmpl +++ b/scripts/templates/CLAUDE.md.tmpl @@ -200,9 +200,9 @@ Neue Lernreihen fuer Fachinformatiker*innen und weitere IT-Ausbildungsberufe wer ## Spec-Kit-Presets / Spec Kit Presets - Standard-Preset-Set: `security-governance` v0.6.1 prio 10, `architecture-governance` v0.5.1 prio 20, `isaqb-architecture-governance` v0.2.1 prio 30, `a11y-governance` v0.4.1 prio 40, `cross-platform-governance` v0.2.1 prio 50, `agent-parity-governance` v0.4.0 prio 60, `autonomous-run-governance` v0.3.2 prio 70, `parallel-autonomous-run-governance` v0.2.3 prio 80. -- Optionale Intake-Presets: `intake-authoring-governance` v0.1.0 prio 64 und `intake-review-governance` v0.1.0 prio 65 bleiben ausserhalb der Standard-Achtermatrix. Die verwaltete Thorsten-Flotte waehlt beide ueber `intake-authoring-ten-governance-presets`; das bisherige Neunerprofil bleibt kompatibel verfuegbar. Neue registrierte Flotten-Repositories erben `defaultPresetProfile`. *Optional intake presets: Authoring v0.1.0 at priority 64 and Review v0.1.0 at priority 65 remain outside the standard eight. Thorsten's managed fleet selects both through the explicit ten-preset profile; the prior nine-preset profile remains available.* +- Optionale Intake-Presets: `intake-authoring-governance` v0.1.1 prio 64 und `intake-review-governance` v0.1.1 prio 65 bleiben ausserhalb der Standard-Achtermatrix. Die verwaltete Thorsten-Flotte waehlt beide ueber `intake-authoring-ten-governance-presets`; das bisherige Neunerprofil bleibt kompatibel verfuegbar. Neue registrierte Flotten-Repositories erben `defaultPresetProfile`. *Optional intake presets: Authoring v0.1.1 at priority 64 and Review v0.1.1 at priority 65 remain outside the standard eight. Thorsten's managed fleet selects both through the explicit ten-preset profile; the prior nine-preset profile remains available.* - Intake Authoring erzeugt aus ausdruecklich benannten, geordneten UTF-8-Quellen genau einen Intake und ein hashgebundenes Receipt. Materielle Fragen werden einzeln und hoechstens fuenfmal pro Durchgang gestellt; offene Fragen erzeugen `NeedsClarification` mit `BLOCKED - DO NOT RUN`. Bestehende Ziele benoetigen ausdrueckliche Update-Autoritaet. `ReadyForReview` ist keine Review-Freigabe. Der naechste Schritt wird als `speckit.intake-review` gemeldet, aber nie automatisch gestartet. Ohne ausdrueckliche Remote-Autoritaet verwendet der erzeugte Autonomous-Prompt `LocalImplementation`. Lernreihen duerfen Intakes vorbereiten und paketieren, starten aber keinen Lernendenlauf ohne ausdruecklichen Auftrag. *Intake Authoring creates one intake and hash-bound receipt from explicitly named ordered UTF-8 sources. It never starts review or execution, never infers overwrite or remote authority, and learner runs still require explicit authorization.* -- Intake Review akzeptiert bei aktiver Projekt- oder Kampagnenpolicy nur aktuelle `Ready`- oder menschlich akzeptierte `ReadyWithAcceptedRisks`-Ergebnisse; Critical/High, offene materielle Fragen, Hash-Drift oder fehlende Worker-Coverage blockieren. Review und Status sind read-only, Repair benoetigt ausdrueckliche Aenderungsautoritaet. +- Intake Review akzeptiert bei aktiver Projekt- oder Kampagnenpolicy nur aktuelle `Ready`- oder menschlich akzeptierte `ReadyWithAcceptedRisks`-Ergebnisse; Critical/High, offene materielle Fragen, Hash-Drift oder fehlende Worker-Coverage blockieren. Review und Status sind read-only, Repair benoetigt ausdrueckliche Aenderungsautoritaet. Series-Reviews verwenden Schema 1.1, binden den normalisierten Request-Hash und pruefen exakte Zielreihenfolge, explizite Roots sowie einen azyklischen Graphen; nicht belegbare Vorgaengerbeziehungen fuehren zu `NeedsClarification`. *Series reviews use schema 1.1, bind the normalized request hash, and verify exact target order, explicit roots, and an acyclic graph; unprovable predecessor relations result in `NeedsClarification`.* - `autonomous-run-governance` v0.3.2 prio 70 ist Teil der Standard-Achtermatrix. Ein vollständiger autonomer Lauf bleibt ausdrücklich delegationspflichtig; die Installation allein erteilt weder Ausführungsberechtigung noch Remote-, Merge-, Bypass- oder Provider-Rechte und `LocalImplementation` bleibt Default. Dokumentations-, Status-, Schema- oder Evidence-Änderungen gelten erst dann als testfrei, wenn keine ausführbaren Validatoren die geänderten Pfade, Marker, Schemas oder Zustandswerte konsumieren. Vor autorisierten Commits wird der exakt beabsichtigte Kandidat mit `git diff --cached --check` und Statusabgleich geprüft; fremde Änderungen bleiben unberührt. Vor einem Merge wird jeder Acceptance-Gate dem tatsächlich ausgeführten Workflow, Job, Runner beziehungsweise der Plattform und dem Befehl zugeordnet; grüne Namen oder ein Bypass ersetzen keinen technischen Nachweis. Bewusst pausierte Läufe werden als `PausedByUser` gespeichert und nur über `speckit.autonomous-resume` fortgesetzt; `speckit.autonomous-stop` wirkt kooperativ am nächsten sicheren Grenzpunkt, und ein gespeicherter Delivery-Modus ist keine aktuelle Berechtigung. Nach Preset- oder Governance-Drift werden neue zwingende Korrektheits-, Sicherheits-, Berechtigungs- und Evidenzregeln minimal mit akzeptierten Plan-, Task- und Checklist-Artefakten abgeglichen; reine Effizienzpräferenzen lösen keine rückwirkende Neugenerierung aus. Die lesbare Skill-Überschrift `Deliver` ist kein Run-State-Wert; für Remote-Closeout gelten ausschließlich `Publish`, `Review` oder `MergeAndSync`. - `parallel-autonomous-run-governance` v0.2.3 prio 80 ist Teil der Standard-Achtermatrix. Die Installation startet keine Kampagne und erteilt keine zusaetzlichen Remote-, Merge-, Bypass-, Abbruch-, Secret- oder Provider-Rechte. Kampagnen bleiben ausdruecklich delegationspflichtig, verwenden getrennte Worktrees und maximal drei gleichzeitig aktive Worker. Schema 1.1 erlaubt ein `runnerProfile` je Worker mit Kampagnen-Fallback; Modell und Reasoning-Stufe sind optionale, nicht geheime Metadaten und werden ohne Deklaration nicht erraten. Konsolidierung verlangt exakten Head, aktuelle Review- und Check-Evidenz, ist nach Teilmerges fortsetzbar und setzt `Completed` erst nach Synchronisation, manifestdeklarierten idempotenten Post-Merge-Aktionen und Abschlussvalidierung. - Reale Preset-8-Kampagnen setzen in jedem Worker-Repository ein installiertes und aktiviertes `autonomous-run-governance >=0.2.2` voraus. Preset 7 mit Prioritaet `70` liefert Lebenszyklus, Evidenz und Berechtigungsgrenzen; Preset 8 mit Prioritaet `80` koordiniert die Kampagne. Fehlt Preset 7, ist es deaktiviert oder zu alt, endet der Preflight vor dem Worker-Start. `requireAutonomousPreset: false` bleibt auf isolierte interne Fixtures begrenzt und ist kein Produktionsmodus. *Real Preset 8 campaigns require installed and enabled `autonomous-run-governance >=0.2.2` in every worker repository. Preset 7 at priority `70` supplies lifecycle, evidence, and authority boundaries; Preset 8 at priority `80` coordinates the campaign. Missing, disabled, or outdated Preset 7 fails preflight before worker start. `requireAutonomousPreset: false` remains limited to isolated internal fixtures and is not a production mode.* diff --git a/scripts/templates/GEMINI.md.tmpl b/scripts/templates/GEMINI.md.tmpl index 8946138..135e876 100644 --- a/scripts/templates/GEMINI.md.tmpl +++ b/scripts/templates/GEMINI.md.tmpl @@ -192,9 +192,9 @@ Neue Lernreihen fuer Fachinformatiker*innen und weitere IT-Ausbildungsberufe wer ## Spec-Kit-Presets / Spec Kit Presets - Standard-Preset-Set: `security-governance` v0.6.1 prio 10, `architecture-governance` v0.5.1 prio 20, `isaqb-architecture-governance` v0.2.1 prio 30, `a11y-governance` v0.4.1 prio 40, `cross-platform-governance` v0.2.1 prio 50, `agent-parity-governance` v0.4.0 prio 60, `autonomous-run-governance` v0.3.2 prio 70, `parallel-autonomous-run-governance` v0.2.3 prio 80. -- Optionale Intake-Presets: `intake-authoring-governance` v0.1.0 prio 64 und `intake-review-governance` v0.1.0 prio 65 bleiben ausserhalb der Standard-Achtermatrix. Die verwaltete Thorsten-Flotte waehlt beide ueber `intake-authoring-ten-governance-presets`; das bisherige Neunerprofil bleibt kompatibel verfuegbar. Neue registrierte Flotten-Repositories erben `defaultPresetProfile`. *Optional intake presets: Authoring v0.1.0 at priority 64 and Review v0.1.0 at priority 65 remain outside the standard eight. Thorsten's managed fleet selects both through the explicit ten-preset profile; the prior nine-preset profile remains available.* +- Optionale Intake-Presets: `intake-authoring-governance` v0.1.1 prio 64 und `intake-review-governance` v0.1.1 prio 65 bleiben ausserhalb der Standard-Achtermatrix. Die verwaltete Thorsten-Flotte waehlt beide ueber `intake-authoring-ten-governance-presets`; das bisherige Neunerprofil bleibt kompatibel verfuegbar. Neue registrierte Flotten-Repositories erben `defaultPresetProfile`. *Optional intake presets: Authoring v0.1.1 at priority 64 and Review v0.1.1 at priority 65 remain outside the standard eight. Thorsten's managed fleet selects both through the explicit ten-preset profile; the prior nine-preset profile remains available.* - Intake Authoring erzeugt aus ausdruecklich benannten, geordneten UTF-8-Quellen genau einen Intake und ein hashgebundenes Receipt. Materielle Fragen werden einzeln und hoechstens fuenfmal pro Durchgang gestellt; offene Fragen erzeugen `NeedsClarification` mit `BLOCKED - DO NOT RUN`. Bestehende Ziele benoetigen ausdrueckliche Update-Autoritaet. `ReadyForReview` ist keine Review-Freigabe. Der naechste Schritt wird als `speckit.intake-review` gemeldet, aber nie automatisch gestartet. Ohne ausdrueckliche Remote-Autoritaet verwendet der erzeugte Autonomous-Prompt `LocalImplementation`. Lernreihen duerfen Intakes vorbereiten und paketieren, starten aber keinen Lernendenlauf ohne ausdruecklichen Auftrag. *Intake Authoring creates one intake and hash-bound receipt from explicitly named ordered UTF-8 sources. It never starts review or execution, never infers overwrite or remote authority, and learner runs still require explicit authorization.* -- Intake Review akzeptiert bei aktiver Projekt- oder Kampagnenpolicy nur aktuelle `Ready`- oder menschlich akzeptierte `ReadyWithAcceptedRisks`-Ergebnisse; Critical/High, offene materielle Fragen, Hash-Drift oder fehlende Worker-Coverage blockieren. Review und Status sind read-only, Repair benoetigt ausdrueckliche Aenderungsautoritaet. +- Intake Review akzeptiert bei aktiver Projekt- oder Kampagnenpolicy nur aktuelle `Ready`- oder menschlich akzeptierte `ReadyWithAcceptedRisks`-Ergebnisse; Critical/High, offene materielle Fragen, Hash-Drift oder fehlende Worker-Coverage blockieren. Review und Status sind read-only, Repair benoetigt ausdrueckliche Aenderungsautoritaet. Series-Reviews verwenden Schema 1.1, binden den normalisierten Request-Hash und pruefen exakte Zielreihenfolge, explizite Roots sowie einen azyklischen Graphen; nicht belegbare Vorgaengerbeziehungen fuehren zu `NeedsClarification`. *Series reviews use schema 1.1, bind the normalized request hash, and verify exact target order, explicit roots, and an acyclic graph; unprovable predecessor relations result in `NeedsClarification`.* - `autonomous-run-governance` v0.3.2 prio 70 ist Teil der Standard-Achtermatrix. Ein vollständiger autonomer Lauf bleibt ausdrücklich delegationspflichtig; die Installation allein erteilt weder Ausführungsberechtigung noch Remote-, Merge-, Bypass- oder Provider-Rechte und `LocalImplementation` bleibt Default. Dokumentations-, Status-, Schema- oder Evidence-Änderungen gelten erst dann als testfrei, wenn keine ausführbaren Validatoren die geänderten Pfade, Marker, Schemas oder Zustandswerte konsumieren. Vor autorisierten Commits wird der exakt beabsichtigte Kandidat mit `git diff --cached --check` und Statusabgleich geprüft; fremde Änderungen bleiben unberührt. Vor einem Merge wird jeder Acceptance-Gate dem tatsächlich ausgeführten Workflow, Job, Runner beziehungsweise der Plattform und dem Befehl zugeordnet; grüne Namen oder ein Bypass ersetzen keinen technischen Nachweis. Bewusst pausierte Läufe werden als `PausedByUser` gespeichert und nur über `speckit.autonomous-resume` fortgesetzt; `speckit.autonomous-stop` wirkt kooperativ am nächsten sicheren Grenzpunkt, und ein gespeicherter Delivery-Modus ist keine aktuelle Berechtigung. Nach Preset- oder Governance-Drift werden neue zwingende Korrektheits-, Sicherheits-, Berechtigungs- und Evidenzregeln minimal mit akzeptierten Plan-, Task- und Checklist-Artefakten abgeglichen; reine Effizienzpräferenzen lösen keine rückwirkende Neugenerierung aus. Die lesbare Skill-Überschrift `Deliver` ist kein Run-State-Wert; für Remote-Closeout gelten ausschließlich `Publish`, `Review` oder `MergeAndSync`. - `parallel-autonomous-run-governance` v0.2.3 prio 80 ist Teil der Standard-Achtermatrix. Die Installation startet keine Kampagne und erteilt keine zusaetzlichen Remote-, Merge-, Bypass-, Abbruch-, Secret- oder Provider-Rechte. Kampagnen bleiben ausdruecklich delegationspflichtig, verwenden getrennte Worktrees und maximal drei gleichzeitig aktive Worker. Schema 1.1 erlaubt ein `runnerProfile` je Worker mit Kampagnen-Fallback; Modell und Reasoning-Stufe sind optionale, nicht geheime Metadaten und werden ohne Deklaration nicht erraten. Konsolidierung verlangt exakten Head, aktuelle Review- und Check-Evidenz, ist nach Teilmerges fortsetzbar und setzt `Completed` erst nach Synchronisation, manifestdeklarierten idempotenten Post-Merge-Aktionen und Abschlussvalidierung. - Reale Preset-8-Kampagnen setzen in jedem Worker-Repository ein installiertes und aktiviertes `autonomous-run-governance >=0.2.2` voraus. Preset 7 mit Prioritaet `70` liefert Lebenszyklus, Evidenz und Berechtigungsgrenzen; Preset 8 mit Prioritaet `80` koordiniert die Kampagne. Fehlt Preset 7, ist es deaktiviert oder zu alt, endet der Preflight vor dem Worker-Start. `requireAutonomousPreset: false` bleibt auf isolierte interne Fixtures begrenzt und ist kein Produktionsmodus. *Real Preset 8 campaigns require installed and enabled `autonomous-run-governance >=0.2.2` in every worker repository. Preset 7 at priority `70` supplies lifecycle, evidence, and authority boundaries; Preset 8 at priority `80` coordinates the campaign. Missing, disabled, or outdated Preset 7 fails preflight before worker start. `requireAutonomousPreset: false` remains limited to isolated internal fixtures and is not a production mode.* diff --git a/scripts/templates/copilot-instructions.tmpl b/scripts/templates/copilot-instructions.tmpl index 4a7579b..404a505 100644 --- a/scripts/templates/copilot-instructions.tmpl +++ b/scripts/templates/copilot-instructions.tmpl @@ -189,9 +189,9 @@ Neue Lernreihen fuer Fachinformatiker*innen und weitere IT-Ausbildungsberufe wer ## Spec-Kit-Presets / Spec Kit Presets - Standard-Preset-Set: `security-governance` v0.6.1 prio 10, `architecture-governance` v0.5.1 prio 20, `isaqb-architecture-governance` v0.2.1 prio 30, `a11y-governance` v0.4.1 prio 40, `cross-platform-governance` v0.2.1 prio 50, `agent-parity-governance` v0.4.0 prio 60, `autonomous-run-governance` v0.3.2 prio 70, `parallel-autonomous-run-governance` v0.2.3 prio 80. -- Optionale Intake-Presets: `intake-authoring-governance` v0.1.0 prio 64 und `intake-review-governance` v0.1.0 prio 65 bleiben ausserhalb der Standard-Achtermatrix. Die verwaltete Thorsten-Flotte waehlt beide ueber `intake-authoring-ten-governance-presets`; das bisherige Neunerprofil bleibt kompatibel verfuegbar. Neue registrierte Flotten-Repositories erben `defaultPresetProfile`. *Optional intake presets: Authoring v0.1.0 at priority 64 and Review v0.1.0 at priority 65 remain outside the standard eight. Thorsten's managed fleet selects both through the explicit ten-preset profile; the prior nine-preset profile remains available.* +- Optionale Intake-Presets: `intake-authoring-governance` v0.1.1 prio 64 und `intake-review-governance` v0.1.1 prio 65 bleiben ausserhalb der Standard-Achtermatrix. Die verwaltete Thorsten-Flotte waehlt beide ueber `intake-authoring-ten-governance-presets`; das bisherige Neunerprofil bleibt kompatibel verfuegbar. Neue registrierte Flotten-Repositories erben `defaultPresetProfile`. *Optional intake presets: Authoring v0.1.1 at priority 64 and Review v0.1.1 at priority 65 remain outside the standard eight. Thorsten's managed fleet selects both through the explicit ten-preset profile; the prior nine-preset profile remains available.* - Intake Authoring erzeugt aus ausdruecklich benannten, geordneten UTF-8-Quellen genau einen Intake und ein hashgebundenes Receipt. Materielle Fragen werden einzeln und hoechstens fuenfmal pro Durchgang gestellt; offene Fragen erzeugen `NeedsClarification` mit `BLOCKED - DO NOT RUN`. Bestehende Ziele benoetigen ausdrueckliche Update-Autoritaet. `ReadyForReview` ist keine Review-Freigabe. Der naechste Schritt wird als `speckit.intake-review` gemeldet, aber nie automatisch gestartet. Ohne ausdrueckliche Remote-Autoritaet verwendet der erzeugte Autonomous-Prompt `LocalImplementation`. Lernreihen duerfen Intakes vorbereiten und paketieren, starten aber keinen Lernendenlauf ohne ausdruecklichen Auftrag. *Intake Authoring creates one intake and hash-bound receipt from explicitly named ordered UTF-8 sources. It never starts review or execution, never infers overwrite or remote authority, and learner runs still require explicit authorization.* -- Intake Review akzeptiert bei aktiver Projekt- oder Kampagnenpolicy nur aktuelle `Ready`- oder menschlich akzeptierte `ReadyWithAcceptedRisks`-Ergebnisse; Critical/High, offene materielle Fragen, Hash-Drift oder fehlende Worker-Coverage blockieren. Review und Status sind read-only, Repair benoetigt ausdrueckliche Aenderungsautoritaet. +- Intake Review akzeptiert bei aktiver Projekt- oder Kampagnenpolicy nur aktuelle `Ready`- oder menschlich akzeptierte `ReadyWithAcceptedRisks`-Ergebnisse; Critical/High, offene materielle Fragen, Hash-Drift oder fehlende Worker-Coverage blockieren. Review und Status sind read-only, Repair benoetigt ausdrueckliche Aenderungsautoritaet. Series-Reviews verwenden Schema 1.1, binden den normalisierten Request-Hash und pruefen exakte Zielreihenfolge, explizite Roots sowie einen azyklischen Graphen; nicht belegbare Vorgaengerbeziehungen fuehren zu `NeedsClarification`. *Series reviews use schema 1.1, bind the normalized request hash, and verify exact target order, explicit roots, and an acyclic graph; unprovable predecessor relations result in `NeedsClarification`.* - `autonomous-run-governance` v0.3.2 prio 70 ist Teil der Standard-Achtermatrix. Ein vollständiger autonomer Lauf bleibt ausdrücklich delegationspflichtig; die Installation allein erteilt weder Ausführungsberechtigung noch Remote-, Merge-, Bypass- oder Provider-Rechte und `LocalImplementation` bleibt Default. Dokumentations-, Status-, Schema- oder Evidence-Änderungen gelten erst dann als testfrei, wenn keine ausführbaren Validatoren die geänderten Pfade, Marker, Schemas oder Zustandswerte konsumieren. Vor autorisierten Commits wird der exakt beabsichtigte Kandidat mit `git diff --cached --check` und Statusabgleich geprüft; fremde Änderungen bleiben unberührt. Vor einem Merge wird jeder Acceptance-Gate dem tatsächlich ausgeführten Workflow, Job, Runner beziehungsweise der Plattform und dem Befehl zugeordnet; grüne Namen oder ein Bypass ersetzen keinen technischen Nachweis. Bewusst pausierte Läufe werden als `PausedByUser` gespeichert und nur über `speckit.autonomous-resume` fortgesetzt; `speckit.autonomous-stop` wirkt kooperativ am nächsten sicheren Grenzpunkt, und ein gespeicherter Delivery-Modus ist keine aktuelle Berechtigung. Nach Preset- oder Governance-Drift werden neue zwingende Korrektheits-, Sicherheits-, Berechtigungs- und Evidenzregeln minimal mit akzeptierten Plan-, Task- und Checklist-Artefakten abgeglichen; reine Effizienzpräferenzen lösen keine rückwirkende Neugenerierung aus. Die lesbare Skill-Überschrift `Deliver` ist kein Run-State-Wert; für Remote-Closeout gelten ausschließlich `Publish`, `Review` oder `MergeAndSync`. - `parallel-autonomous-run-governance` v0.2.3 prio 80 ist Teil der Standard-Achtermatrix. Die Installation startet keine Kampagne und erteilt keine zusaetzlichen Remote-, Merge-, Bypass-, Abbruch-, Secret- oder Provider-Rechte. Kampagnen bleiben ausdruecklich delegationspflichtig, verwenden getrennte Worktrees und maximal drei gleichzeitig aktive Worker. Schema 1.1 erlaubt ein `runnerProfile` je Worker mit Kampagnen-Fallback; Modell und Reasoning-Stufe sind optionale, nicht geheime Metadaten und werden ohne Deklaration nicht erraten. Konsolidierung verlangt exakten Head, aktuelle Review- und Check-Evidenz, ist nach Teilmerges fortsetzbar und setzt `Completed` erst nach Synchronisation, manifestdeklarierten idempotenten Post-Merge-Aktionen und Abschlussvalidierung. - Reale Preset-8-Kampagnen setzen in jedem Worker-Repository ein installiertes und aktiviertes `autonomous-run-governance >=0.2.2` voraus. Preset 7 mit Prioritaet `70` liefert Lebenszyklus, Evidenz und Berechtigungsgrenzen; Preset 8 mit Prioritaet `80` koordiniert die Kampagne. Fehlt Preset 7, ist es deaktiviert oder zu alt, endet der Preflight vor dem Worker-Start. `requireAutonomousPreset: false` bleibt auf isolierte interne Fixtures begrenzt und ist kein Produktionsmodus. *Real Preset 8 campaigns require installed and enabled `autonomous-run-governance >=0.2.2` in every worker repository. Preset 7 at priority `70` supplies lifecycle, evidence, and authority boundaries; Preset 8 at priority `80` coordinates the campaign. Missing, disabled, or outdated Preset 7 fails preflight before worker start. `requireAutonomousPreset: false` remains limited to isolated internal fixtures and is not a production mode.* diff --git a/scripts/templates/speckit-workflow-section.md b/scripts/templates/speckit-workflow-section.md index 31701e9..0b7e7d8 100644 --- a/scripts/templates/speckit-workflow-section.md +++ b/scripts/templates/speckit-workflow-section.md @@ -34,7 +34,7 @@ Workspace-Familie ist: | `parallel-autonomous-run-governance` | Parallel Autonomous Run Governance | `v0.2.3` | `80` | Optional koennen `intake-authoring-governance` v0.1.1 mit Prioritaet `64` und -`intake-review-governance` v0.1.0 mit Prioritaet `65` zwischen Agent Parity und +`intake-review-governance` v0.1.1 mit Prioritaet `65` zwischen Agent Parity und Preset 7 installiert werden. Beide bleiben ausserhalb der Standard-Achtermatrix. Authoring erzeugt aus ausdruecklich benannten geordneten UTF-8-Quellen genau einen Intake und ein Receipt, startet aber keine Folgeaktion. Review prueft den @@ -42,6 +42,10 @@ gespeicherten Intake unabhaengig. Das Registry-Profil `intake-authoring-ten-governance-presets` waehlt beide; das bisherige Neunerprofil bleibt kompatibel. Lernendenlaeufe bleiben separat beauftragungspflichtig. +Series-Reviews verwenden Request und Result nach Schema 1.1. Der normalisierte +Request-Hash, Zielrollen, exakte Reihenfolge, Roots und der azyklische +Vorgaengergraph werden gemeinsam validiert; unklare Beziehungen fuehren zu +`NeedsClarification`. `autonomous-run-governance` ist Teil der Standard-Achtermatrix. Vollständige autonome Läufe bleiben ausdrücklich delegationspflichtig. `LocalImplementation` ist der @@ -122,12 +126,15 @@ workspace family is: | `parallel-autonomous-run-governance` | Parallel Autonomous Run Governance | `v0.2.3` | `80` | Optionally install `intake-authoring-governance` v0.1.1 at priority `64` and -`intake-review-governance` v0.1.0 at priority `65` between Agent Parity and +`intake-review-governance` v0.1.1 at priority `65` between Agent Parity and Preset 7. Both remain outside the standard eight. Authoring creates one intake and receipt from explicit ordered UTF-8 sources without starting a downstream command. Review evaluates that intake independently. Registry profile `intake-authoring-ten-governance-presets` selects both; the prior nine-preset profile remains compatible. Learner runs still require explicit authorization. +Series reviews use schema 1.1 for request and result. They jointly validate the +normalized request hash, target roles, exact order, roots, and the acyclic +predecessor graph; ambiguous relations result in `NeedsClarification`. `autonomous-run-governance` is part of the standard eight-preset matrix. Complete autonomous runs still require explicit delegation. `LocalImplementation` is the safe default; diff --git a/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-report.md b/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-report.md new file mode 100644 index 0000000..c09bddb --- /dev/null +++ b/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-report.md @@ -0,0 +1,34 @@ +# Intake Review Report: Active Level-2 Lastenhefte + +## Ergebnis / Outcome + +`Ready` + +Alle 13 aktiven Intakes wurden einzeln und als geordnete Serie +geprueft. Identitaet, Ziel, Scope, Nicht-Ziele, Anforderungen, Abnahme, +Evidence, Delivery Authority und Prompt-Vertrag sind fuer spaetere getrennte +Spec-Kit-Laeufe ausreichend bestimmt. Es bleiben keine offenen Findings, +Fragen oder akzeptierten Risiken. + +*All 13 active intakes were reviewed individually and as an +ordered series. No open finding, question, or accepted risk remains.* + +## Abdeckung / Coverage + +- Aktive Intakes: 13/13 +- Ausgeschlossene Archive, Ordnungs- oder Uebersichtsdokumente: 5 +- Intake-Creator-Marker und gueltige Receipts: 13/13 +- Specify- und Autonomous-Prompts: 13/13 +- Delivery Authority: 13/13 `LocalImplementation` +- Offene Fragen, Findings und akzeptierte Risiken: 0 + +## Grenzen / Boundaries + +Dieser Review startet weder Specify noch Autonomous oder Implement. Archivierte +Features und nicht einzeln startbare Lernreihen-/Track-Landkarten bleiben +unveraendert. Jeder spaetere Lauf benoetigt weiterhin eine aktuelle, +ausdrueckliche Benutzerfreigabe. + +*This review starts no later command. Historical features and non-executable +series maps remain unchanged. Every later run still requires current explicit +user authorization.* diff --git a/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-request.json b/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-request.json new file mode 100644 index 0000000..7548239 --- /dev/null +++ b/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-request.json @@ -0,0 +1,83 @@ +{ + "schemaVersion": "1.0", + "reviewId": "65b3feff-d1a0-4d1f-a5bb-d473b95a6edb", + "mode": "Series", + "policy": "inventar-worker-active-lastenhefte-v1", + "targets": [ + { + "path": "Lastenheft_Secure-Development-Hardening.md", + "role": "Primary" + }, + { + "path": "Lastenheft_Sandbox-gestuetzte-Secure-Development-Haertung.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_Didactic-Inline-Code-Comment-Hardening.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_TG_Elmish_Entscheidung.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_TG_Migration_InventarViewerApp.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_TG_Migration_CtrlWorkerServiceCmdlet.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_TG_Migration_CtrlWorkerServiceApp.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_A11Y_TUI_API.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_Statistik_View_Lesemethoden.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_IDbService_Interface.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_MongoDB_Paritaet.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_RL-SE-Checklist-Selbstpruefung.md", + "role": "OrderedMember" + }, + { + "path": "Lastenheft_GSDB-Spec-Kit-Intensivpruefung.md", + "role": "OrderedMember" + } + ], + "series": { + "orderedTargetPaths": [ + "Lastenheft_Secure-Development-Hardening.md", + "Lastenheft_Sandbox-gestuetzte-Secure-Development-Haertung.md", + "Lastenheft_Didactic-Inline-Code-Comment-Hardening.md", + "Lastenheft_TG_Elmish_Entscheidung.md", + "Lastenheft_TG_Migration_InventarViewerApp.md", + "Lastenheft_TG_Migration_CtrlWorkerServiceCmdlet.md", + "Lastenheft_TG_Migration_CtrlWorkerServiceApp.md", + "Lastenheft_A11Y_TUI_API.md", + "Lastenheft_Statistik_View_Lesemethoden.md", + "Lastenheft_IDbService_Interface.md", + "Lastenheft_MongoDB_Paritaet.md", + "Lastenheft_RL-SE-Checklist-Selbstpruefung.md", + "Lastenheft_GSDB-Spec-Kit-Intensivpruefung.md" + ], + "dependencies": [] + }, + "campaign": { + "manifestPath": "N/A", + "workers": [], + "operatorExceptions": [] + } +} diff --git a/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-result.json b/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-result.json new file mode 100644 index 0000000..5b55bc6 --- /dev/null +++ b/specs/intake-normalization-20260723/history/65b3feff-d1a0-4d1f-a5bb-d473b95a6edb/intake-review-result.json @@ -0,0 +1,126 @@ +{ + "schemaVersion": "1.0", + "reviewId": "65b3feff-d1a0-4d1f-a5bb-d473b95a6edb", + "mode": "Series", + "status": "Ready", + "policy": "inventar-worker-active-lastenhefte-v1", + "reviewedAt": "2026-07-23T14:32:13Z", + "repository": { + "root": ".", + "head": "e5a35d2f090ed3ec6529837fb12e9a0e247b03ba" + }, + "targets": [ + { + "path": "Lastenheft_Secure-Development-Hardening.md", + "role": "Primary", + "normalizedSha256": "9eb32515a590aada78a5b33170e00aace834ce87d9b5f5ed9535445c6e50380f", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_Sandbox-gestuetzte-Secure-Development-Haertung.md", + "role": "OrderedMember", + "normalizedSha256": "3166f34e5d7003cfe28738785c322e3c3fd1431b7e2dec80c738cf0ec342268d", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_Didactic-Inline-Code-Comment-Hardening.md", + "role": "OrderedMember", + "normalizedSha256": "a2c55dbb43be5a77cd7cc02722af48fc00c20089ad853b7bda26a44d667abf94", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_TG_Elmish_Entscheidung.md", + "role": "OrderedMember", + "normalizedSha256": "17cd456fdf35b788666096abe67d1cb8b2a338dcc61be8bb84bf9e53ab62d374", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_TG_Migration_InventarViewerApp.md", + "role": "OrderedMember", + "normalizedSha256": "9e4054f42c0d091efe7f7940b8dc6855e899a9926cd7c8feabcc3d52e9fc72a0", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_TG_Migration_CtrlWorkerServiceCmdlet.md", + "role": "OrderedMember", + "normalizedSha256": "bca3cf505503fe10c3504e6c03a1e8c6922e10121fe2b3b9610b82de110934aa", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_TG_Migration_CtrlWorkerServiceApp.md", + "role": "OrderedMember", + "normalizedSha256": "d0431f7a5c02730f7c1141c1ce9d61a8aca328a3c554dd13e6318ac5bbf8675c", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_A11Y_TUI_API.md", + "role": "OrderedMember", + "normalizedSha256": "6ee7cbadf3327b81924a2ed8963f53f6be9026e373a916aa987e4cb54a26b211", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_Statistik_View_Lesemethoden.md", + "role": "OrderedMember", + "normalizedSha256": "062f43f7910cab4c10c60e5e527cc6df1396537243e70d3fdf52dd5b5fe9ed6a", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_IDbService_Interface.md", + "role": "OrderedMember", + "normalizedSha256": "62cb6a19e3535f12a295176a5146bac46add45dcbdd2932e04841ac2f0c2985a", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_MongoDB_Paritaet.md", + "role": "OrderedMember", + "normalizedSha256": "b28296fe4883e6aeb16f02a5f70c27fea949a31e8cea0a237b693c18d30cdd7d", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_RL-SE-Checklist-Selbstpruefung.md", + "role": "OrderedMember", + "normalizedSha256": "7581944e869f50cf8ce1c3d9cd510956c68b3ccb66ec03fe746f7138465ca024", + "gitBlob": "N/A" + }, + { + "path": "Lastenheft_GSDB-Spec-Kit-Intensivpruefung.md", + "role": "OrderedMember", + "normalizedSha256": "ffd78d52538010696e63642296258776ea49fe3c3e831f1b93f1d7793c271ed0", + "gitBlob": "N/A" + } + ], + "findings": [], + "questions": [], + "acceptedRisks": [], + "operatorExceptions": [], + "coverage": { + "individual": [ + "Lastenheft_Secure-Development-Hardening.md", + "Lastenheft_Sandbox-gestuetzte-Secure-Development-Haertung.md", + "Lastenheft_Didactic-Inline-Code-Comment-Hardening.md", + "Lastenheft_TG_Elmish_Entscheidung.md", + "Lastenheft_TG_Migration_InventarViewerApp.md", + "Lastenheft_TG_Migration_CtrlWorkerServiceCmdlet.md", + "Lastenheft_TG_Migration_CtrlWorkerServiceApp.md", + "Lastenheft_A11Y_TUI_API.md", + "Lastenheft_Statistik_View_Lesemethoden.md", + "Lastenheft_IDbService_Interface.md", + "Lastenheft_MongoDB_Paritaet.md", + "Lastenheft_RL-SE-Checklist-Selbstpruefung.md", + "Lastenheft_GSDB-Spec-Kit-Intensivpruefung.md" + ], + "series": [ + "All 13 active paths and their exact order", + "Repository ordering, predecessor gates, overlap, terminology, authority, and no-auto-start boundaries", + "Excluded archive and overview classifications from target-manifest.json" + ], + "workers": [] + }, + "summary": { + "critical": 0, + "high": 0, + "medium": 0, + "low": 0 + }, + "supersedes": "N/A" +} diff --git a/specs/intake-normalization-20260723/intake-review-report.md b/specs/intake-normalization-20260723/intake-review-report.md index c09bddb..b9f53b7 100644 --- a/specs/intake-normalization-20260723/intake-review-report.md +++ b/specs/intake-normalization-20260723/intake-review-report.md @@ -32,3 +32,19 @@ ausdrueckliche Benutzerfreigabe. *This review starts no later command. Historical features and non-executable series maps remain unchanged. Every later run still requires current explicit user authorization.* +## Schema-1.1-Graphbindung / Schema 1.1 graph binding + +- Neuer Review / New review: `07138bdd-24c3-4693-bb2f-38271144a94e` +- Ersetzt / Supersedes: `65b3feff-d1a0-4d1f-a5bb-d473b95a6edb` +- Gebundene Ziele / Bound targets: 13 +- Wurzeln / Roots: 1 +- Abhaengigkeitskanten / Dependency edges: 12 +- Request-Bindung / Request binding: repository-relativer Pfad und normalisierter SHA-256 + +Die Reihenfolge und der azyklische Request-Graph wurden gegen die +repository-eigenen Reihenfolge- und Lernreihendokumente neu geprueft. Der +Review startet keinen nachgelagerten Spec-Kit-Lauf. + +*The order and acyclic request graph were revalidated against the repository's +own ordering and learning-series documents. This review starts no downstream +Spec Kit command.* diff --git a/specs/intake-normalization-20260723/intake-review-request.json b/specs/intake-normalization-20260723/intake-review-request.json index 7548239..22c6ff5 100644 --- a/specs/intake-normalization-20260723/intake-review-request.json +++ b/specs/intake-normalization-20260723/intake-review-request.json @@ -1,6 +1,6 @@ { - "schemaVersion": "1.0", - "reviewId": "65b3feff-d1a0-4d1f-a5bb-d473b95a6edb", + "schemaVersion": "1.1", + "reviewId": "07138bdd-24c3-4693-bb2f-38271144a94e", "mode": "Series", "policy": "inventar-worker-active-lastenhefte-v1", "targets": [ @@ -73,7 +73,71 @@ "Lastenheft_RL-SE-Checklist-Selbstpruefung.md", "Lastenheft_GSDB-Spec-Kit-Intensivpruefung.md" ], - "dependencies": [] + "roots": [ + "Lastenheft_Secure-Development-Hardening.md" + ], + "dependencies": [ + { + "from": "Lastenheft_Secure-Development-Hardening.md", + "to": "Lastenheft_Sandbox-gestuetzte-Secure-Development-Haertung.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_Sandbox-gestuetzte-Secure-Development-Haertung.md", + "to": "Lastenheft_Didactic-Inline-Code-Comment-Hardening.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_Didactic-Inline-Code-Comment-Hardening.md", + "to": "Lastenheft_TG_Elmish_Entscheidung.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_TG_Elmish_Entscheidung.md", + "to": "Lastenheft_TG_Migration_InventarViewerApp.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_TG_Migration_InventarViewerApp.md", + "to": "Lastenheft_TG_Migration_CtrlWorkerServiceCmdlet.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_TG_Migration_CtrlWorkerServiceCmdlet.md", + "to": "Lastenheft_TG_Migration_CtrlWorkerServiceApp.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_TG_Migration_CtrlWorkerServiceApp.md", + "to": "Lastenheft_A11Y_TUI_API.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_A11Y_TUI_API.md", + "to": "Lastenheft_Statistik_View_Lesemethoden.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_Statistik_View_Lesemethoden.md", + "to": "Lastenheft_IDbService_Interface.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_IDbService_Interface.md", + "to": "Lastenheft_MongoDB_Paritaet.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_MongoDB_Paritaet.md", + "to": "Lastenheft_RL-SE-Checklist-Selbstpruefung.md", + "kind": "Precedes" + }, + { + "from": "Lastenheft_RL-SE-Checklist-Selbstpruefung.md", + "to": "Lastenheft_GSDB-Spec-Kit-Intensivpruefung.md", + "kind": "Precedes" + } + ] }, "campaign": { "manifestPath": "N/A", diff --git a/specs/intake-normalization-20260723/intake-review-result.json b/specs/intake-normalization-20260723/intake-review-result.json index 5b55bc6..2adedff 100644 --- a/specs/intake-normalization-20260723/intake-review-result.json +++ b/specs/intake-normalization-20260723/intake-review-result.json @@ -1,13 +1,13 @@ { - "schemaVersion": "1.0", - "reviewId": "65b3feff-d1a0-4d1f-a5bb-d473b95a6edb", + "schemaVersion": "1.1", + "reviewId": "07138bdd-24c3-4693-bb2f-38271144a94e", "mode": "Series", "status": "Ready", "policy": "inventar-worker-active-lastenhefte-v1", - "reviewedAt": "2026-07-23T14:32:13Z", + "reviewedAt": "2026-07-23T17:46:14Z", "repository": { "root": ".", - "head": "e5a35d2f090ed3ec6529837fb12e9a0e247b03ba" + "head": "37bd5029ed1d0ed23c574d29fef1dd403b8647c5" }, "targets": [ { @@ -122,5 +122,9 @@ "medium": 0, "low": 0 }, - "supersedes": "N/A" + "supersedes": "65b3feff-d1a0-4d1f-a5bb-d473b95a6edb", + "requestEvidence": { + "path": "specs/intake-normalization-20260723/intake-review-request.json", + "normalizedSha256": "4bad374bb72e7ff3014bf6ec7ccc4b9031f6cb97e8d720905dfe54b530c5dee1" + } } From 2a65138a1caed8e0c32a465a540b014275e1250e Mon Sep 17 00:00:00 2001 From: Thorsten Hindermann Date: Thu, 23 Jul 2026 19:46:31 +0200 Subject: [PATCH 2/2] docs: render project statistics --- docs/project-statistics.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/project-statistics.md b/docs/project-statistics.md index b476f06..4b0f79a 100644 --- a/docs/project-statistics.md +++ b/docs/project-statistics.md @@ -446,27 +446,27 @@ Profil 2 verwendet Git-getrackte Textdateien und sichtbare Git-Aktivitaet. Die W | Kennzahl / Metric | Wert / Value | |---|---:| -| Textbasis / Text base | 119678 lines | -| Textdateien / Text files | 787 | +| Textbasis / Text base | 120634 lines | +| Textdateien / Text files | 790 | | Beobachtbarer Zeitraum / Observable period | 2025-07-27..2026-07-23 | | Aktivtage / Active days | 126 | -| Relevante Commits / Relevant commits | 675 | -| Zeilen je Aktivtag / Lines per active day | 949.8 | +| Relevante Commits / Relevant commits | 676 | +| Zeilen je Aktivtag / Lines per active day | 957.4 | | Peak-Tag im Fenster / Peak day in window | 2026-04-19 / 107974 | | Peak-Woche im Fenster / Peak week in window | 2026-04-19 / 114277 | | Laengste Serie / Longest streak | 11 days | -| Speedup vs. 80 lines/day | 11.9x | -| Speedup vs. 100 lines/day | 9.5x | -| Methodik / Methodology | v2; source `9db4a433c8a7` | +| Speedup vs. 80 lines/day | 12.0x | +| Speedup vs. 100 lines/day | 9.6x | +| Methodik / Methodology | v2; source `e2aa3f9010a2` | ### Artefaktmix / Artifact Mix ```text Produktiv / Production [##..................] 8.5% | 10220 Tests [#...................] 3.5% | 4174 -Dokumentation / Documentation [###############.....] 76.1% | 91055 +Dokumentation / Documentation [###############.....] 76.0% | 91734 Skripte / Scripts [##..................] 8.1% | 9747 -Konfiguration / Configuration [#...................] 1.7% | 2079 +Konfiguration / Configuration [#...................] 2.0% | 2356 Daten und Medien / Data and media [#...................] 1.0% | 1150 Sonstiger Text / Other text [#...................] 1.0% | 1253 ``` @@ -590,8 +590,8 @@ Die festen Slots halten den Phasenvergleich auch bei fehlenden oder spaeter erga ```text Scale: 0..20x -80 lines/day [############........] 11.9x -100 lines/day [#########...........] 9.5x +80 lines/day [############........] 12.0x +100 lines/day [##########..........] 9.6x ``` Die Faktoren vergleichen sichtbare Lieferdichte mit den dokumentierten manuellen Referenzen. Sie messen keine Arbeitszeit. @@ -604,7 +604,7 @@ Die Faktoren vergleichen sichtbare Lieferdichte mit den dokumentierten manuellen Scale: 0..1000 lines/day Experienced manual [##..................] 80 Thorsten solo [##..................] 100 -Visible repository [###################.] 949.8 +Visible repository [###################.] 957.4 ``` Die gemeinsame Skala vergleicht Referenzen und sichtbare Lieferdichte. Sie schreibt die Git-Aktivitaet keiner Person oder KI pauschal zu. @@ -630,6 +630,6 @@ DE: Das Fenster beginnt am 2025-07-27 und endet am 2026-07-23. Es enthaelt 126 a | 2026-04 | 119744 | | 2026-05 | 13510 | | 2026-06 | 37685 | -| 2026-07 | 50490 | +| 2026-07 | 51636 |