Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5c3679a
fix(consent): reuse command grants across variants and dispatches (#439)
datacas Nov 9, 2026
5b6ff3a
fix: scope compound grants and retry invalid consent answers
datacas Nov 9, 2026
496c6fc
fix(permissions): ask after unresolved cd, match globs per component,…
datacas Nov 9, 2026
b15056e
fix(permissions): narrow unresolved-directory names, parse env option…
datacas Nov 9, 2026
9ad0e26
fix(permissions): resolve tilde prefixes as bash, guard env -C behind…
datacas Nov 9, 2026
0079aef
fix(permissions): keep ~{…} guarded, decode $'…' and line continuatio…
datacas Nov 9, 2026
901c400
docs(permissions): describe decoded shell quoting in the credential g…
datacas Nov 9, 2026
26f5855
fix(permissions): never raise or warn on bash-only escapes in $'...' …
datacas Nov 9, 2026
b0716d7
fix(permissions): bash-exact $'…' decoding, continuations inside $ ex…
datacas Nov 9, 2026
d733968
docs(permissions): env runners and bash-exact quoting in the credenti…
datacas Nov 9, 2026
87067ba
fix: guard decoded shell settings and extended globs
datacas Nov 9, 2026
d7ad729
fix: preserve shell expansion boundaries in consent guards
datacas Nov 9, 2026
62f727f
Merge branch 'main' into task-439
datacas Nov 9, 2026
b2efad7
fix(consent): load the regex parser without the removed sre_parse shi…
datacas Nov 9, 2026
05d4cab
fix(consent): keep case clauses inside substitutions from ending them…
datacas Nov 9, 2026
48b3c0f
fix: retain case substitution guards across Bash command prefixes
datacas Nov 10, 2026
16ded03
fix(permissions): preserve loop body command boundaries (#439)
datacas Nov 10, 2026
a1c0dc0
fix(permissions): peel reserved-word headers before segment guards (#…
datacas Nov 10, 2026
cca777c
fix(permissions): keep quoting when deciding shell syntax from words …
datacas Nov 10, 2026
38e8c9f
fix(permissions): session tool-results reads and cycle-only scope for…
datacas Nov 10, 2026
e9fc02c
fix(permissions): harden session tool-results handling after review (…
datacas Nov 10, 2026
c83bf6b
docs(permissions): state only the tool-results checks the hook enforc…
datacas Nov 10, 2026
770e6eb
Merge branch 'main' into task-439
datacas Nov 10, 2026
96edec2
test(consent): build the session tool-results path from the platform …
datacas Nov 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/ROADMAP_V2.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,12 @@ La cola de refactorización #186–#190 se integra según necesidad, sin bloqueo

La asignación de una issue a un milestone expresa el objetivo de planificación, no convierte condiciones ni recomendaciones en bloqueos. No se fijan fechas.

M1 update for #439 and #455: #439 now owns configured-command variant
matching, validated pattern grants and compound shell consent with per-segment
guards (PA-19). #455 is absorbed for implementation and stays open until the
integrated Claude and Codex native parity checks are verified; its earlier
exact-only compound restriction is superseded by #439's approved scope.

| Milestone | Contenido y criterio de salida |
|---|---|
| [M1 — Safe, inspectable execution](https://github.com/datacas/code-cycle-toolkit/milestone/1) | #327–#330, #259/#261/#290/#294, presupuestos #262 y overrides #285; #326 coordina. Runtime fixes found while running #330: #385 (P0, manual checks restore and idempotent resume; blocks #330), #382 (host-attested verification evidence) and #383 (malformed stage questions), #388 (P2, manual-check name grammar and resume-history tests; follow-up to #387) and #389 (P1, quota-aware routing and controlled fallback handoff on resume; blocks resolving REV-001 on #384), #394 (P1, resume pending verification when a stored check has no authorized command binding) #395 (P2, quota recovery commands accept repository-registered models), #402 (P2, Orca Task-spec reader binding and stock-CLI documentation; follow-up to #330), #393 (P0, urgent, resume blocked on frozen check identity; fixed in #404) and #406 (P0, urgent, ask the user instead of blocking on any command outside a stage's command table; builds on #289; implemented in #409), plus the stops reported on 2026-11-04, all urgent: #410 (P1, execution plan receipt rejected without naming the field), #411 (P1, remote-ref inspection of the review workspace without retry), #412 (P1, stage report rejected on schema grounds ends BLOCKED after a push), #413 (P1, another rereview of the same head instead of the CI gate), #414 (P1, required verification a review stage cannot run loops on resume), #415 (P1, `cycle_status.py --line` mixes cycles of other repositories) and #416 (P1, `basedpyright` does not fit the verification limit); #383 and #364 are urgent as well. Follow-ups found while running them: #421 (P1, urgent, a base-branch advance by another actor fails running stages as a publication breach or read-only violation), #425 (P1, urgent, an accepted `not_verified` check ends a rereview as `HUMAN_INTERVENTION` instead of `VERIFICATION_INCOMPLETE`) and #422 (P2, `read_operands` misses file operands behind abbreviated grep long options and combined jq short flags; follow-up to #406) and #428 (P0, urgent, interim guidance for the `credential_reads` remedy when `issue_review` cannot read the gh store; docs, skills and test only; the runtime fix is #397), #397 (P0, urgent, `issue_review` reads private GitHub issue data through a runtime snapshot instead of the gh credential store), #398 (P0, urgent, ask for consent to run an authorized Docker Compose `run` verification command instead of refusing it) and #435 (P1, urgent, a `READY` issue review is rejected when the runtime adds `permission_outcomes` to its result; follow-up to #406), #426 (P2, how a cycle reacts to an external base-branch advance: branch update and revalidation; follow-up to #421), #432 (P2, a remote inspection failure after review masks a changed implementer HEAD or working tree; found in #411) and #433 (P2, a review report with an integer `checks[].attempts` is rejected after a completed approval; follow-up to #412) and #377 (P1, per-project and per-check verification timeouts; moved from M2 because the 10-minute limit stopped two consecutive stages on a passing `basedpyright` run that needs 12 min 25 s, see #416), #446 (P1, bounded re-read that confirms a review or rereview publication before retrying; split from #364), #450 (P0, urgent, core principle of agent capability parity: audit and remove the toolkit's own vetoes of what the agent can run) with its child issues for the vetoes no earlier issue tracked, listed in `docs/capability-parity-audit.md`: #455 (P1, consent for compound shell commands instead of a parse-time refusal), #456 (P1, consent for Git, gh and Docker table refusals), #457 (P1, the never-grant boundaries become consent with a risk warning and a record), #458 (P2, run with a disclosed reduced guarantee when the configuration defeats the hook) and #459 (P1, normalize report and receipt representations instead of rejecting them), and #187 (P1, split `CycleRecorder.stage` into typed helpers; moved from M7 because that one 755-line method makes the `basedpyright` check take more than 10 minutes, target 10 s or less, see #416 and #377) and #438 (P0, urgent, one malformed stage receipt in an accumulating comment hides every receipt next to it and blocks a clean review; receipts are read one by one, related to #409 and #412), and #460 (P2, clear a stale declined-check record on a later allow and disclose consented `$TMPDIR` prefixes; follow-up to #398) and #439 (P0, urgent, a grant covers its variants: pattern (regular expression) grants, reads outside the stage workspace and layer attribution of refusals; follow-up to #406) and #464 (P0, urgent, a finished stage ends `BLOCKED` on refused read-only commands and detached runs cannot answer consent; follow-up to #406 and #450)) and #469 (P1, `plan_projection` raises `AttributeError` on a non-object `evidence` item and a finished stage loses its report) and #470 (P1, urgent, the parallel CI gate ends `CI_UNREADABLE` when the plan has no branch-protection API; fall back to declared or SHA check runs; case of #450). Cinco perfiles resolubles, plan inspeccionable, ejecución nativa/Orca coherente, controles obligatorios no reducibles y resume trazable. #147 es independiente y #189 recomendable. Correcciones operativas asignadas siguen condicionadas a reproducción. |
Expand Down
10 changes: 6 additions & 4 deletions docs/capability-parity-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ given instead of line numbers so the references stay valid.
| PA-16 | A dirty implementer checkout blocks the preparation of a review clone | `executors.py` review clone preparation | A review judges a committed head, not uncommitted edits | #142 | role contract | — |
| PA-17 | A success receipt needs the remote head, the local HEAD and the PR head to be one commit | `scripts/publication.py`, `scripts/reroute.py` `observe_publication` | A publication is claimed only after it is observed | #284 | role contract | — |
| PA-18 | A program outside the stage table, or a read outside the stage workspace, is refused. Since #464 the read-only forms of `sed`, `date` and `ps` are in the table (PA-40) | `stage_permissions._check_words`, rule `command_table` | Closed default table | #406: refused `sed`, `cp`, `rm`, `sha256sum`, `pwd` stopped cycles | toolkit veto | #406 (converted by #409), #439, #441 |
| PA-19 | Compound shell syntax is refused at parse time and never offered as a question: pipes, `&&`, `;`, heredocs, command and process substitution, expansions other than `$TMPDIR`, unquoted globs, redirections outside `$TMPDIR` | `stage_permissions.parse_command`; `protected_reason` re-parses, so `refused_operation` skips it | Keeps one inspectable command per call | #450 (a pipe to `head`, a heredoc), #364, #187 | toolkit veto | #455, #431 (quoted glob pathspecs) |
| PA-19 | #439 converts compound shell syntax to interpreter consent; visible segments and substitutions retain concrete guards. A regex needs explicit compound approval; simple-command grants never extend to other segments. Native parity remains pending, so the audit stays open. Redirects outside `$TMPDIR` retain their concrete guard | `stage_permissions.parse_command`, `protected_reason` | Per-segment analysis and whole-operation consent | #450 (a pipe to `head`, a heredoc), #364, #187 | toolkit veto | #439 absorbs #455; #455 stays open until verified, #431 |
| PA-43 | Reads outside the stage workspace request exact or validated pattern consent; trailing slash, flags and another read tool can share an approved directory matcher. Reads under the current Claude session's own tool-results directory are allowed like `$TMPDIR` reads, and an operand there is a placeholder; a grant naming a dispatch- or session-unique path is offered for this cycle or as a pattern only | `stage_permissions.check`, `ConsentStore` | Role-scoped disclosure and consent | #439 | role contract | #439 |
| PA-20 | Git subcommands and forms outside the table are refused and not offered as questions: `ls-tree`, `stash`, `worktree add`, `branch` other than `--show-current`, `merge` in implement, other `pull` and `fetch` forms, global options other than `--no-pager` | `stage_permissions._check_git`, re-run by `protected_reason` | Closed Git table | #431 | toolkit veto | #431, #456 |
| PA-21 | `gh` reads beyond the table or the cycle's artefacts are refused and not offered as questions: `gh issue list` and `gh search` outside `issue_review`, `gh api` GET of other endpoints, `gh pr edit <n>` before the cycle knows its PR number | `stage_permissions._check_gh` and `GH_TABLE`, re-run by `protected_reason` | Closed `gh` table | #440; this audit's own implement stage was refused `gh issue list` on 2026-11-06 | toolkit veto | #440, #456 |
| PA-22 | `gh` writes outside the cycle's artefacts are refused and not offered as questions: `gh issue create`, comments on related issues or other change requests | `GH_TABLE` roles and `_check_gh` scope | Stage publication boundary | #450: the implement stage could not create the child issues this issue asks for | toolkit veto | #456 |
Expand All @@ -62,7 +63,7 @@ given instead of line numbers so the references stay valid.
| PA-25 | Every force-push variant is refused and cannot be granted | `stage_permissions._check_push` | Publication integrity | #142 | toolkit veto | #457 |
| PA-26 | A push to the base branch, `main` or `master` is refused and cannot be granted | `stage_permissions._check_push` | Publication integrity | #142 | toolkit veto | #457 |
| PA-27 | Ref deletion is refused and cannot be granted: `push --delete`, an empty-source refspec, `--prune`, `--mirror`, `--all`, `branch -d/-D`, `tag -d`, `update-ref -d` | `stage_permissions._check_push`, `protected_reason` | Publication integrity | #142 | toolkit veto | #457 |
| PA-28 | Reads of a protected credential store cannot be granted (`~/.ssh`, `~/.aws`, `~/.codex/auth.json`, `~/.claude/.credentials.json`, `~/.claude.json`, and `~/.config/gh` outside publishing stages); Claude's sandbox and Codex's profile deny them too | `stage_permissions.credential_reason`, rule `credential_guard`; `credential_read_denials`; Codex `none` filesystem rules; since #397, issue review reads a GitHub work item from the runtime's [issue snapshot](provider-contract.md#runtime-issue-snapshot) instead of the `gh` store, which stays denied | Credential confidentiality | #294, #397, #428 | toolkit veto | #457 |
| PA-28 | Reads of a protected credential store cannot be granted (`~/.ssh`, `~/.aws`, `~/.codex/auth.json`, `~/.claude/.credentials.json`, `~/.claude.json`, and `~/.config/gh` outside publishing stages); Claude's sandbox and Codex's profile deny them too. #439 covers static glob/brace operands matched one component at a time and possible directories after cd or stack operations, including `builtin`/`command` and `env -C` forms (also behind any program other than `echo`/`printf`/`grep`/`rg`), `~user`/`~+` tilde prefixes, and words and path/glob settings decoded from `$'…'`/`$"…"` quoting and line continuations. `nocaseglob` from decoded words or inherited `BASHOPTS` compares globs without case; extglob components conservatively match any name. An assigning builtin may set any path/glob setting, and computed `shopt`/`set` options may enable either glob mode; unrelated settings can therefore retain the guard for credential-like operands. After a directory change it cannot resolve (variables, `CDPATH`, a reassigned `HOME`/`OLDPWD`/`PWD`, `~-`/`~N` prefixes, `eval`/`source`/`trap`), only relative operands that may name a store keep this guard, and the rest, including `.`, are asked with a warning; a subcommand word spelled like a store file (`git config`) is still refused there | `stage_permissions.credential_reason`, rule `credential_guard`; `credential_read_denials`; Codex `none` filesystem rules; since #397, issue review reads a GitHub work item from the runtime's [issue snapshot](provider-contract.md#runtime-issue-snapshot) instead of the `gh` store, which stays denied | Credential confidentiality | #294, #397, #428, #439 | toolkit veto | #457 |
| PA-29 | Edits to `.git`, `~/.gitconfig` or `$XDG_CONFIG_HOME/git` cannot be granted | `stage_permissions.check_file_edit`, `protected_reason` | `core.fsmonitor` or `diff.external` would make an allowed `git status` run a program | #142 | toolkit veto | #457 |
| PA-30 | A `forever` grant is refused for `review` and `rereview`, and a stored global entry for them is ignored | `grants.NO_GLOBAL_ROLES` | Reviews judge untrusted changes in any repository | #406 | toolkit veto | #457 |
| PA-31 | When Claude's Bash sandbox cannot start, static-only mode denies verification, Docker, arbitrary-code tool choices and publication, and the checks become `not_verified` | `executors.py` (`static_only=True`); `stage_permissions._check_words` and `_check_git` | No repository code runs unconfined; no host-side push | #345, #348 | toolkit veto | #291 (checks), #457 (publication) |
Expand Down Expand Up @@ -104,6 +105,7 @@ Why each real limit and role contract stays.
| PA-39 | A `READY` judged on a partial work item would be a claim about input nobody read. The stop names the missing data and the provider's real failure; no command of the stage is refused, its credential denials are unchanged, and `--issue-review off` remains the user's choice. |
| PA-41 | With no one attached, a question has no answerer, which is the same limit an agent run directly in `-p` mode meets when its permission layer denies a command. The toolkit grants nothing implicitly, records the denial and the question for later, and never waits indefinitely. A saved grant (#406) applies first, and `docker_host` and configured-check consent keep `permission_required`. |
| PA-42 | The resume point is evidence the runtime wrote after reading the stage's structured result, not a permission. Using it on a moved head would review or fix code the earlier stage never produced, so a mismatch stops and is named. `--no-auto-resume` starts the work item over and `--from <stage> --pr <n>` resumes by hand, so the user keeps every option. |
| PA-43 | A stage discloses reads outside its assigned workspace and the user can authorize an exact command or reusable pattern. This is a role contract with consent, and a stored answer removes the toolkit question without widening host or OS permissions. |

## Vetoes: conversion and parity baseline

Expand All @@ -128,7 +130,7 @@ the real repository.
| ID | Conversion | Claude baseline | Codex baseline |
|---|---|---|---|
| PA-18 | Done in #409: the request is offered as **this cycle / this repository / forever / deny**, recorded, and skipped with `not_verified` checks when denied | `claude -p` runs `sha256sum README.md` in the worktree | `codex exec -s workspace-write` runs `sha256sum README.md`; a Codex stage has no command table, so the test pins that it stays equal |
| PA-19 | Offer the exact command string as an interpreter-style grant, with a warning that the hook cannot inspect what the shell does inside it; record it; never generalize it | `claude -p` runs `git log --oneline \| head -5` and a heredoc `cat <<EOF` into `$TMPDIR` | `codex exec -s workspace-write` runs the same two commands |
| PA-19 | #439: consent for whole shell source, optionally a user-approved validated pattern; guards analyse each segment and substitutions. #455 stays open pending native verification | `claude -p` runs `git log --oneline \| head -5` and a heredoc `cat <<EOF` into `$TMPDIR` | `codex exec -s workspace-write` runs the same two commands |
| PA-20 | Offer table-membership refusals as exact grants; keep argument guards as warnings shown in the question | `claude -p` runs `git ls-tree HEAD` and `git stash list` | `codex exec -s workspace-write` runs the same |
| PA-21 | Offer reads beyond the table and the cycle as exact grants, labelled read-only | `claude -p` runs `gh issue list --repo <cycle repo> --state open` | `codex exec -s workspace-write` with the network route a publishing stage has runs the same |
| PA-22 | Offer writes outside the cycle as exact grants, with the target artefact named and a risk warning | `claude -p` runs `gh issue create --repo <fixture repo> --title t --body b` | `codex exec -s workspace-write` runs the same against the fixture repository |
Expand Down Expand Up @@ -188,7 +190,7 @@ milestone table of [ROADMAP_V2](ROADMAP_V2.md).

| ID | Draft | Title | Milestone | Priority | Rows | Scope |
|---|---|---|---|---|---|---|
| #455 | CI-1 | Offer consent for compound shell commands instead of refusing them at parse time | M1 — Safe, inspectable execution | P1 | PA-19 | When `parse_command` refuses pipes, chaining, heredocs, substitutions, expansions, unquoted globs or redirections, build an exact, never-generalized operation from the command string and ask with the interpreter warning. Record the decision. Add one parity test against the PA-19 baselines. |
| #455 | CI-1 | Compound shell consent absorbed by #439; stays open until integrated verification | M1 — Safe, inspectable execution | P1 | PA-19 | #439 owns shared simple/compound consent, segment guards and validated pattern grants. Record the interpreter warning and decision; verify both native executor baselines before closing #455. |
| #456 | CI-2 | Offer consent for Git, gh and Docker table refusals | M1 — Safe, inspectable execution | P1 | PA-20, PA-21, PA-22, PA-23, PA-24 | Stop `protected_reason` from turning table membership and argument guards into refusals that are never asked about. Ask with the guard's risk as the warning: executing options, writes outside the cycle, Docker as root. Keep each grant exact and recorded. Add one parity test per row. #431 and #440 stay the narrower fixes they track. #452 already asks about Docker forms outside the table, so the Docker part left is a Compose command whose configuration cannot be resolved (PA-24). |
| #457 | CI-3 | Convert the never-grant boundaries to consent with a risk warning and a record | M1 — Safe, inspectable execution | P1 | PA-25, PA-26, PA-27, PA-28, PA-29, PA-30, PA-31 | Ask about force-pushes, base pushes, ref deletion, credential reads, Git config edits, `forever` grants for review roles, and publication without a confirmed sandbox. After consent, run with no further veto. The publication audit reports an authorized operation as recorded, not as a breach. Code-host protection stays a real limit. Add one parity test per row. |
| #458 | CI-4 | Run with a disclosed reduced guarantee instead of stopping when the configuration defeats the hook | M1 — Safe, inspectable execution | P2 | PA-32 | Name the setting that defeats the hook or the sandbox. Ask once per scope, record the decision, and dispatch. A Codex review whose clone confinement cannot be proven gets the same question rather than `BLOCKED`. |
Expand Down
Loading
Loading