Commit Graph
86 Commits
Author SHA1 Message Date
sysadminandClaude Opus 4.8 33d01ba65e feat: add controller issue-acceptance gate (Closes #500)
Add issue_acceptance_gate validation for Controller Issue Acceptance
comments, final-report rules blocking merge-only issue completion claims,
documentation/templates, and regression tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-08 03:27:37 -04:00
sysadminandClaude Opus 4.8 0cbd1fc801 feat: add first-class stacked PR support to author workflow (#484)
Normal author work is unchanged: gitea_lock_issue still requires the worktree
to be base-equivalent to master/main/dev, and gitea_create_pr still targets a
normal base branch. A *stacked* PR (based on another unmerged PR's branch) is a
new explicit, proof-backed path — it never bypasses the issue lock.

- stacked_pr_support.py: pure policy. Approve a non-master base only when it is
  declared AND owned by a live OPEN PR whose number matches; reject arbitrary,
  mismatched, or stale (merged/closed) branches; require the PR body to document
  the stack (base branch, stacked-on PR, merge ordering).
- issue_lock_worktree.py: read_worktree_git_state / _find_matching_base_ref gain
  an opt-in extra_bases arg so an approved stacked base can anchor
  base-equivalence in addition to master/main/dev (empty by default = unchanged).
- gitea_mcp_server.py:
  - gitea_lock_issue gains stacked_base_branch + stacked_base_pr. When declared,
    it verifies the open PR owns the branch, anchors base-equivalence to it, and
    records approved_stacked_base = {branch, pr_number, verified_open} on the lock.
  - gitea_create_pr validates a non-master base against the lock's approved
    stacked base, re-checks the dependency PR is still open, and enforces the
    stacked-PR body fields. Master-based path untouched.
- Docs: llm-workflow-runbooks.md and work-issue.md document when stacked PRs are
  allowed and the required PR-body wording.

Tests: test_stacked_pr_support.py (18 policy cases), stacked base-equivalence in
test_issue_lock_worktree.py, approved_stacked_base persistence round-trip in
test_issue_lock_store.py. Existing lock/create_pr regressions still pass.

The #482 -> #479/#478 case motivates this: create_pr previously could not open a
stacked PR because the lock required a master-equivalent worktree.

Closes #484.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-08 02:22:54 -04:00
sysadminandClaude Opus 4.8 d042a9ca24 feat: replace global issue lock with keyed persistent store (Closes #443)
Store per remote/org/repo/issue locks under GITEA_ISSUE_LOCK_DIR with
atomic writes and per-session binding. Integrate own-branch adoption for
lock recovery, update worktree-start and cleanup reconcile, and add tests
documenting the ban on manual global lock seeding.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 17:30:35 -04:00
sysadminandClaude Opus 4.8 795f544047 feat: block manual issue-lock seeding and require lock disclosure (#447)
Add lock_provenance metadata on gitea_lock_issue writes and fail closed at
gitea_create_pr when provenance is missing. Final-report validation now
requires explicit External-state mutations disclosure for issue-lock
read/write/delete and blocks mixed author PR creation with reviewer approval.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 16:18:42 -04:00
sysadminandClaude Opus 4.8 c91e3642de feat: add live PR/issue queue dashboard to web UI (Closes #429)
Adds read-only /queue and /api/queue routes that load open PRs and issues
from the default registry project via gitea_auth, surface pagination proof,
and classify items with claimed/blocked/in-review/duplicate badges.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 15:05:41 -04:00
sysadminandClaude Opus 4.8 6670c72e65 feat: add canonical workflow prompt library to web UI (Closes #428)
Expose short copy/paste operator prompts on /prompts derived from canonical
workflow files with workflow path and SHA-256 hash. Adds JSON export at
/api/prompts, copy buttons, and tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 14:53:16 -04:00
sysadmin 16dcf65825 feat: add web UI project registry and onboarding (#427)
Load projects from versioned webui/data/projects.registry.json with profile
mappings, workflow/schema paths, and read-only onboarding checklist UI.
Seeds Gitea-Tools; exposes /projects, /projects/{id}, and /api/projects.

Closes #427
2026-07-07 14:44:27 -04:00
sysadminandClaude Opus 4.8 a8fcf0e01c feat: add internal web UI server skeleton (Closes #426)
Starlette read-only MVP with shared layout, /health JSON liveness, and
route stubs for projects, prompts, runtime, audit, worktrees, and leases.
Includes scripts/run-webui, docs/webui-local-dev.md, and tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 13:23:09 -04:00
sysadmin 6a37f7c8d5 fix: resolve conflicts for PR #388
Merge prgs/master into feat/issue-304-reconciler-profile; keep both
reconciler_profile and reconciliation_workflow imports in gitea_mcp_server.py.
2026-07-07 11:19:57 -04:00
sysadmin 6e212c0de7 Merge pull request 'feat: enforce author work leases in issue lock preflight (Closes #267)' (#386) from feat/issue-267-work-leases into master 2026-07-07 09:52:37 -05:00
sysadmin d814a9ca81 fix: resolve conflicts for PR #388
Merge prgs/master; unify reconciler profile docs (#304) with master's
already-landed close tool guidance (#310).
2026-07-07 10:34:57 -04:00
sysadmin 9ff861a1f3 fix: resolve conflicts for PR #388
Merge prgs/master into feat/issue-304-reconciler-profile; keep both
reconciler_profile and review_merge_state_machine imports.

Closes conflict with master landing review_merge_state_machine (#389 stack).
2026-07-07 10:19:40 -04:00
sysadmin 167257b8a3 Merge commit 'b5ee6567aade4fdce4c57d826184bfd01ebb522e' into HEAD
# Conflicts:
#	tests/test_resolve_task_capability.py
2026-07-07 10:18:20 -04:00
sysadminandClaude Opus 4.8 91c67930ec feat: add PR-only queue cleanup mode with per-PR dispatch gates (Closes #390)
Adds canonical pr-queue-cleanup workflow, report verifier, reviewer-only task
routing, and runbook guidance so operators can drain open PRs one canonical
review at a time with fail-closed boundaries on batching and unauthorized merges.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 10:02:49 -04:00
sysadminandClaude Opus 4.8 21ff12cef7 feat: add prgs-reconciler profile model and role detection (Closes #304)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 09:39:52 -04:00
sysadmin 9cf1b41b77 feat: add prgs-reconciler profile and gated already-landed PR close (Closes #310)
Introduce a dedicated reconciler role with gitea_reconcile_already_landed_pr,
which closes open PRs only after live fetch and ancestor proof against a fresh
target branch. Document the gitea-reconciler namespace and extend task routing.
2026-07-07 09:20:50 -04:00
sysadmin 0cc3ed8868 feat: enforce author work leases (Closes #267) 2026-07-07 05:35:44 -04:00
sysadmin cbae9a3ecd Merge pull request 'docs: pin GlitchTip filing orchestrator contract (Closes #153)' (#343) from feat/issue-153-glitchtip-filing-orchestrator into master 2026-07-07 04:05:22 -05:00
sysadmin edf26410a0 merge: resolve master conflicts for #259 subagent tool-budget docs
Integrate split-workflow router SKILL.md (#333) with subagent tool-budget
guardrails (#259). Keep shell spawn hard-stop (#258) and subagent
delegation (#266) operator-guide rules alongside subagent_tool_budget.
2026-07-07 04:35:46 -04:00
sysadmin 873bcd06e5 docs: pin GlitchTip filing orchestrator contract (Closes #153) 2026-07-07 04:27:02 -04:00
sysadmin 2a28fee7f2 Merge pull request 'docs: add Shell Spawn Hard-Stop Rule for agent workflows (Closes #258)' (#281) from docs/issue-258-shell-spawn-hard-stop into master 2026-07-07 01:18:50 -05:00
sysadmin 3354da1973 Merge branch 'master' into docs/issue-258-shell-spawn-hard-stop 2026-07-07 01:00:06 -05:00
sysadmin d0e250e860 Merge branch 'master' into docs/issue-260-forbid-commit-fallbacks 2026-07-07 00:59:30 -05:00
sysadminandClaude Opus 4.8 82e6d3a3f4 docs: add subagent tool-budget guardrails for MCP tasks (Closes #259)
Document default tool-call and wall-time budgets for deterministic MCP
work, forbid subagent commit delegation when gitea_commit_files is
available, and pin the rules in runbooks, portable skill, operator guide,
and doc-contract tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 00:17:26 -04:00
sysadminandClaude Opus 4.8 cf292e4166 docs: add Shell Spawn Hard-Stop Rule for agent workflows (#258)
Documents fail-closed behavior when the shell executor fails to spawn
(exit_code: -1, empty stdout/stderr): probe once, mark shell unavailable,
hard-stop after two consecutive spawn failures, and emit a recovery report
(restart session, kill hung background terminals, prefer MCP-native paths)
instead of retry-spiralling.

- skills/llm-project-workflow/SKILL.md: portable rule section
- docs/llm-workflow-runbooks.md: Gitea runbook section
- gitea_mcp_server.py: shell_spawn_hard_stop operator guide rule
- tests/test_shell_spawn_hard_stop_docs.py: doc-contract tests (5)
- tests/test_operator_guide.py: require the new guide key

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 14:52:12 -04:00
sysadminandClaude Opus 4.8 2f4672218c docs: forbid improvised commit fallbacks when MCP commit exists (#260)
Document MCP-native gitea_commit_files as the only approved commit path when
gitea.repo.commit is allowed; ban WebFetch, Playwright, and manual base64
workarounds in runbooks and safety model. Add doc tests.

Closes #260.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 14:44:41 -04:00
sysadminandClaude Opus 4.8 850b7c34b6 docs: agent temp artifact cleanup guidance and detection (#261)
Add runbook section, root-level .gitignore patterns, and detection helper
for _encode_/_emit_/_inline_ throwaway scripts. Surface non-blocking
warnings from assess_preflight_status and gitea_lock_issue.

Closes #261

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 14:42:33 -04:00
sysadminandClaude Opus 4.8 f464716e89 docs: document agent temp artifact cleanup and gitignore patterns (#261)
Add runbook guidance for deleting throwaway _encode/_emit/_inline commit
helpers after MCP commit attempts, ignore those patterns in .gitignore,
and cross-link the worktree-cleanup template.

Closes #261.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 14:42:22 -04:00
sysadmin 183e9f08b8 Merge pull request 'docs: separate Jenkins trigger onto jenkins-write-mcp boundary (#152)' (#264) from codex/issue-152-jenkins-trigger-boundary into master 2026-07-06 13:26:16 -05:00
sysadminandClaude Opus 4.8 216fa5cf46 docs: add Global LLM Worktree Rule to operating rules (#272)
Require mutations only from branches/ worktrees after proving project
root, cwd, branch, stable main-checkout branch, and session worktree
path. Surface in portable skill, runbooks, wiki, operator guide, and
start-issue template.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 14:09:44 -04:00
sysadminandClaude Opus 4.8 2111c84e7d docs: add Work Selection Rule for LLM operating rules
Require lease verification (open PRs, branches, worktrees, leases,
merged completion) before any issue or PR work. Surface the rule in
the portable skill, Gitea runbooks, operator guide, and start-issue
template; add doc-contract tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 13:45:25 -04:00
sysadminandClaude Opus 4.8 b4e04f4dfb docs: separate Jenkins trigger onto jenkins-write-mcp boundary (#152)
Document that build triggers live on the gated jenkins-write-mcp server,
not the read-only jenkins-mcp surface. Add registration and safety-model
cross-links plus doc tests that fail if trigger tools reappear on the read
boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 13:26:58 -04:00
sysadmin d5d3331498 docs(wiki): refresh Gitea Wiki publication proof heads (closes #224) 2026-07-06 01:49:59 -04:00
sysadminandClaude Opus 4.8 342974f840 docs(wiki): record Gitea-Tools live wiki publication proof (#224)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 01:36:49 -04:00
sysadminandClaude Opus 4.8 f58317d079 Add docs/wiki source tree and Gitea Wiki sync helper (#224)
Bootstrap repo-tracked wiki pages (10 required pages), the operator-
confirmed sync script, PR template publication gate, and safety tests so
Gitea-Tools can publish to the live Gitea Wiki per issue #224.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-06 01:36:10 -04:00
sysadmin d747e0a2c9 Document external MCP registration for Jenkins and GlitchTip 2026-07-06 01:07:46 -04:00
sysadmin 2cd61a90fe feat(runtime-isolation): loader, health checks, tests, and documentation for Issue #221 2026-07-05 21:13:40 -04:00
sysadminandClaude Fable 5 484873ed73 Add explicit close_pr capability resolution and gated PR close path (Issue #216)
PR closure had no first-class capability: agents could close PRs through
gitea_edit_pr(state=closed) with no close-specific capability proof, and
gitea_resolve_task_capability(close_pr) failed as unknown, leaving the
broad edit path as an untracked close fallback.

Add close_pr to the resolver TASK_MAP (gitea.pr.close, author-side) and
gate gitea_edit_pr(state=closed) on the same operation: without it the
close fails closed before any auth or API call, with reasons and a
structured permission_report; with it the close proceeds and is audited
as a distinct close_pr action carrying the required capability. Reject
invalid state values outright so case variants cannot bypass the gate.
Generalize the profile gate helper (_profile_operation_gate) and document
the PR comment / PR edit / PR close capability split.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 17:18:48 -04:00
sysadmin 29f04d003a Merge pull request 'Harden LLM role/task alignment and PR review feedback discovery' (#169) from feat/issue-167-review-feedback-discovery into master 2026-07-05 12:17:43 -05:00
sysadminandClaude Fable 5 a4060c59cc feat: harden LLM role/task alignment and PR review feedback discovery (#167)
- Add gitea_get_pr_review_feedback: read-only MCP-native discovery of
  formal PR review verdicts (APPROVED / REQUEST_CHANGES / COMMENT) with
  latest-state-per-reviewer, blocking change-request and approval
  summaries, reviewed-head vs current-head staleness, dismissed/PENDING
  handling, credential redaction of review bodies, no endpoint URLs
  without the reveal opt-in, and a fail-closed gitea.read gate that
  returns feedback_not_attempted with a structured #142 permission
  report and zero API calls.
- Add task/role guards to gitea_resolve_task_capability: new
  stop_required and task_role_guidance outputs, and a distinct
  address_pr_change_requests author task, so a review/merge request
  under an author profile returns explicit STOP guidance instead of
  silently degrading into author-side mutations, and author-side
  remediation is explicitly barred from review verdicts and merges.
- Add build_validation_report: validation reporting helper that
  distinguishes passed / failed / skipped / not-run, requires the exact
  output for failures, rejects vague statuses, and never implies
  full-suite success unless the full-suite command passed.
- Document task/role alignment (review vs author-fix vs merge vs
  comment workflows with required identity, allowed/forbidden actions,
  and stop conditions), MCP-native review feedback discovery, and
  validation reporting discipline in docs/llm-workflow-runbooks.md.
- Add tests/test_review_feedback.py (22 tests) covering discovery,
  staleness, redaction, URL hiding, fail-closed gating, role guards for
  author/reviewer profiles, and validation report semantics.

Closes #167

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 12:59:54 -04:00
sysadmin 194301359e feat: harden PR queue reconciliation against stale/conflicting state (#166)
- Add updated_at (list_prs), + merged_at/merge_commit_sha/closed_at/updated_at (view_pr) for staleness detection in tool output and inventory.
- Surface updated_at in gitea_review_pr inventory report.
- Strengthen gitea-pr-review skill steps, _COMMON_WORKFLOWS, _GUIDE_RULES with explicit live reconciliation checklist, 'do not trust prior handoffs', stop on conflict.
- Add dedicated live queue reconciliation runbook section in docs/llm-workflow-runbooks.md.
- Add passthrough tests and assertions for new fields.
- All gates, redaction, author/reviewer separation untouched.
- Tests pass, py_compile clean, diff clean, secret sweep clean.

Closes #166
2026-07-05 12:45:05 -04:00
sysadmin 7102f27d20 docs: update operator runbooks and roadmap docs for Jenkins/GlitchTip MCP status (#154)
- Updated jenkins-readonly and glitchtip-readonly skills in _PROJECT_SKILLS:
  - Descriptions and notes now use actual server names (jenkins-mcp, glitchtip-mcp)
  - Explain historical -readonly skill names vs registration in mcp-control-plane #55
  - Note gated trigger in jenkins-mcp (see #56), partial filing in glitchtip (see #57)
- Updated docs/safety-model.md: naming note, corrected mutation gating claims for trigger/filing
- Updated docs/architecture/jenkins-readonly-build-status-design.md: naming note, updated Phase 1 to account for gated trigger
- No behavior change to skill status/availability; no secrets exposed
- AC: stale naming explained, actual names documented, Jenkins claims corrected, GlitchTip filing as partial, no over-claiming

Closes #154
2026-07-05 10:59:50 -04:00
sysadminandClaude Fable 5 2ad876be9f docs: document static dual-namespace Gitea MCP deployment model (#143)
Record the #139 accepted decision as a deployment doc: two static
per-role MCP namespaces (gitea-author, gitea-reviewer), one credential
per process, with dynamic profile switching and dispatcher routing
explicitly rejected for now. Covers rationale (audit clarity, credential
concentration, two-party review boundary, fail-closed behavior),
reference-only client setup via GITEA_MCP_CONFIG/GITEA_MCP_PROFILE,
the 'Auth unsupported' client-badge caveat, and reconnect/reload
requirements after profile/config/code changes. Cross-linked from the
execution-profiles model doc and the workflow runbooks; guarded by
docs-content tests.

Closes #143

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 03:23:40 -04:00
sysadmin 3953b6bdd2 feat: allow gitea.issue.comment for author and reviewer profiles (#137)
- Add aliases 'issue.comment' and 'issue_comment' to GITEA_OPERATION_ALIASES so short names normalize to gitea.issue.comment
- Grant 'issue.comment' (and short) in v2 example profiles for example-author and example-reviewer
- Grant in gitea-mcp.example.json mdcps-reviewer
- Update docs example profiles
- Add test coverage for new aliases and normalization

This enables gitea_create_issue_comment and list for profiles that include it, while preserving fail-closed for others and separation of duties (issue.comment does not imply pr.review etc).

Closes #137
2026-07-05 02:10:18 -04:00
sysadmin 4253f8a52a feat: satisfy Issue #129 operator guide and skill registry requirements 2026-07-04 17:46:19 -04:00
sysadmin a0e7d3360e feat: implement profile activation and runtime identity clarity (#131) 2026-07-04 02:04:07 -04:00
sysadminandClaude Fable 5 6a8a9d99b7 feat: add operator guide and project skills discovery MCP tools (#128)
Add three read-only capability-discovery tools so new LLM sessions can
learn the workflow rules and available project skills from the MCP
server instead of long pasted operator prompts:

- mcp_get_control_plane_guide: active profile, authenticated identity
  (fail-soft; unresolved identity returns STOP instructions),
  allowed/forbidden operations, profile-aware guidance (author profiles
  are told review/approve/merge is forbidden; reviewer profiles are told
  review/merge requires eligibility checks and a pinned head SHA; mixed
  profiles get a misconfiguration warning), and the standing rules: hard
  stops, fail-closed behavior, head-SHA pinning, merge confirmation,
  redaction, author/reviewer/merger separation, profile switching, and
  identity verification.
- mcp_list_project_skills: registry of ten project workflows (issue
  authoring, PR creation, PR review, PR merge, issue comments, profile
  switching, redaction/security review, Jenkins read-only, GlitchTip
  read-only, release/operator) with description, when-to-use, required
  operations, status, and per-profile availability. Unimplemented
  services are listed as designed-not-implemented rather than omitted.
- mcp_get_skill_guide: step-by-step guide per skill; unknown names fail
  closed with the list of valid names.

All three are read-only and change no existing gate or permission.
Normal output contains no endpoint URLs or keychain IDs; the guide
includes the server host only under GITEA_MCP_REVEAL_ENDPOINTS=1.

Tests (tests/test_operator_guide.py, 17 new): profile-aware guidance
for author/reviewer, unresolved-identity STOP, read-only behavior,
redaction defaults and reveal opt-in, rules coverage, registry
completeness and profile awareness, unimplemented-service marking,
fail-closed unknown skill names.

Docs: llm-workflow-runbooks.md now tells new sessions to call the guide
tools first.

Closes #128

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 19:49:11 -04:00
sysadminandClaude Fable 5 5aeb51f132 feat: add Gitea issue comment list/create MCP tools (#126)
Add gitea_list_issue_comments and gitea_create_issue_comment so
discussion/design workflows can read and post issue comments through
the MCP layer instead of direct API scripts.

- List requires gitea.read; create requires gitea.issue.comment —
  gated separately from the gitea.pr.* review/merge family, fail closed.
- Issue comments never touch PR review endpoints.
- LLM-safe output: comment id/author/timestamps/body only; web links
  appear solely under the GITEA_MCP_REVEAL_ENDPOINTS admin opt-in.
- Create operations are audit-logged (create_issue_comment) and errors
  are redacted before being raised.
- Tests cover list/create success, permission blocks (including PR
  review permissions not granting issue comments), forbidden-overrides,
  empty body, missing issue with redacted error, endpoint separation,
  and reveal opt-in.
- Document issue comments versus PR reviews in
  docs/gitea-execution-profiles.md.

Closes #126

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 19:07:36 -04:00
sysadmin 9f75e28094 Merge pull request 'docs: re-land release version SOP with v1.1.0 audit lessons (#111)' (#119) from docs/issue-111-release-version-sop into master 2026-07-03 16:09:40 -05:00
sysadminandClaude Fable 5 e0861bcb03 feat: operation-name normalization table with fail-closed enforcement (#106)
Promote the #103 minimal alias map to the documented public table
GITEA_OPERATION_ALIASES and add the #106 enforcement layer:

- normalize_operation(op, service): canonical namespaced names; legacy
  spellings accepted only via the explicit table; unknown, ambiguous,
  and cross-service names fail closed.
- check_operation(op, allowed, forbidden, service): normalizes BOTH the
  requested operation and the profile lists before any membership
  check; forbidden always overrides allowed; unnormalizable allowed
  entries grant nothing and unnormalizable forbidden entries deny the
  request, so normalization can never silently widen permissions;
  empty/missing allowed list denies everything.
- gitea_check_pr_eligibility now routes its capability check through
  check_operation, fixing the mismatch where canonical namespaced
  profile ops (gitea.pr.merge) never matched the raw action (merge)
  and namespaced forbidden entries were never enforced.
- Document the normalization table and enforcement rules in
  docs/gitea-execution-profiles.md, replacing the stale 'enforcement
  out of scope' caveat.
- tests/test_op_normalization.py: full #106 matrix (27 tests) —
  qualified/legacy allowed and forbidden, unknown, ambiguous, service
  mismatch, forbidden-overrides-allowed, empty/missing allowed,
  duplicates after normalization, no permission widening, and
  eligibility integration proving normalization happens before
  enforcement. Existing v1/env unqualified behaviour stays compatible.

Closes #106

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 03:35:03 -04:00