From ede10d9c0b3fb781deb7b7c06befc752edd70346 Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sun, 25 Oct 2026 12:55:11 +0200 Subject: [PATCH 1/4] fix(port): drop the second copy of the run_in_background token rule rules.sed carried the same `run_in_background: true` token rule twice. The first copy consumes every match, so the second could never fire, and no exemption fragment can tell two identical lines apart. Tree unchanged. --- omp-port/rules.sed | 1 - 1 file changed, 1 deletion(-) diff --git a/omp-port/rules.sed b/omp-port/rules.sed index fcacceb..5729118 100644 --- a/omp-port/rules.sed +++ b/omp-port/rules.sed @@ -155,7 +155,6 @@ s#`readonly: true`#read-only posture carried in the brief#g s#`readonly: false`#full tools per spawn#g s#agent mode \(`readonly: false`\)#full tools per spawn#g s#`readonly`: `true`#read-only posture carried in the brief#g -s#`run_in_background: true`#one `task` call with all items in `tasks[]`#g s#readonly strips MCPs\.#nothing strips MCPs here.#g # A judge is spawned with read posture in its brief, never as a field. Matched on the field name # rather than the sentence, because upstream rewords the sentence and the field never survives it. From e3be341a8080f18b3a9374a8b103ae889a2722c4 Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sun, 25 Oct 2026 12:56:18 +0200 Subject: [PATCH 2/4] fix(port): match the interrogate reviewers net on the path upstream writes Both `interrogate reviewers` line rules matched `task.agentModelOverrides` in `~/.omp/agent/config.yml`, text only the path net further down writes, so neither could fire and one exemption fragment covered both. The surviving rule now matches the `~/.cursor/rules/pstack-models.mdc` path upstream writes, like the runner and judge rules beside it, and is no longer stale. The other copy goes, with the rule that only rewrote its output and both exemptions. Tree unchanged: the per-entry sentence rule consumes the one upstream occurrence first. --- omp-port/rules.sed | 4 +--- omp-port/stale-exempt.tsv | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/omp-port/rules.sed b/omp-port/rules.sed index 5729118..c798e51 100644 --- a/omp-port/rules.sed +++ b/omp-port/rules.sed @@ -366,7 +366,6 @@ s#the `(architect|arena|swarm|interrogate) runners` line in the `pstack-models\. s#the `interrogate reviewers` line in the `pstack-models\.mdc` rule#one exact discovered reviewer agent per configured entry#g s#the `arena cross-judge pool` line in `~?/?\.cursor/rules/pstack-models\.mdc`#an exact discovered judge agent whose configured model differs from the parent's when the roster provides one, in `~/.omp/agent/config.yml`#g s#the `(architect|arena|swarm|interrogate) runners` line in `~?/?\.cursor/rules/pstack-models\.mdc`#the exact discovered runner agents in `~/.omp/agent/config.yml`#g -s#the `interrogate reviewers` line in `task\.agentModelOverrides` in `~/\.omp/agent/config\.yml`#one exact discovered reviewer agent per entry in `~/.omp/agent/config.yml`#g # The last two Cursor role labels. Same reasoning as the runner lines above: the label is a key # in a rule file that omp does not have, so naming it names nothing. The capability it stood for # is what the port writes instead. @@ -374,10 +373,9 @@ s#the `how (explorer|explainer)` line#an exact discovered \1 agent in `~/.omp/ag s#one per entry on the `interrogate reviewers` line in `~?/?(\.cursor/rules/pstack-models\.mdc|task\.agentModelOverrides in ~?/?\.omp/agent/config\.yml)`#one per configured entry in `~/.omp/agent/config.yml`#g s#Set `model` to that line.s value, or to the default if the rule or the line is missing\.#Each discovered agent's frontmatter or `task.agentModelOverrides[]` selects its model; the item has no `model` field.#g s#Leave `model` unset when the value is `auto` or `inherit-parent`\.#An `auto` or `inherit-parent` entry means the parent chat model, so omit that role from the map.#g -s#the `interrogate reviewers` line in `task\.agentModelOverrides` in `~?/?\.omp/agent/config\.yml`#one exact discovered reviewer agent per configured entry in `~/.omp/agent/config.yml`#g +s#the `interrogate reviewers` line in `~?/?\.cursor/rules/pstack-models\.mdc`#one exact discovered reviewer agent per configured entry in `~/.omp/agent/config.yml`#g s#the `architect runners` line in the `task\.agentModelOverrides` entries in `~/\.omp/agent/config\.yml`#the same exact discovered runner agents Arena uses, with the architect role in each brief, in `~/.omp/agent/config.yml`#g s#an exact discovered (why investigators|why synthesizer|interrogate reviewers|swarm workers|architect runners|arena runners|arena cross-judge pool|reflect tooling|reflect judgment, divergent, synthesizer) agent#an exact discovered agent filling that role#g -s#one per entry on one exact discovered reviewer agent per entry#one per configured entry#g # A single spawn is still one task call with one item and the required shared context. Matched on # the "spawn one " phrase, which is the shape upstream uses everywhere it means one worker, # rather than on the sentence around it. diff --git a/omp-port/stale-exempt.tsv b/omp-port/stale-exempt.tsv index e96d328..b4af722 100644 --- a/omp-port/stale-exempt.tsv +++ b/omp-port/stale-exempt.tsv @@ -70,9 +70,7 @@ For each candidate, read the first JSONL line and check th superseded: upstream the `arena cross-judge pool` line in the `pstack-models\.m forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. the `swarm workers` model and speed \(your fast code model forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. the `interrogate reviewers` line in the `pstack-models\.md forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. -the `interrogate reviewers` line in `task\.agentModelOverr superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. the `architect runners` line in the `task\.agentModelOverr superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. -one per entry on one exact discovered reviewer agent per e superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. `agent`: an exact discovered (how explorer|how explainer) superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. re-read this playbook from trunk with `git show origin/mai superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. Re-read the execution playbook from trunk and the armed /g superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. From 7304cc156ba4a0a9a23cbbf873e39809f8f0379a Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sun, 25 Oct 2026 12:57:29 +0200 Subject: [PATCH 3/4] fix(port): pin each shared exemption fragment to the rule it names Five fragments matched a second rule, either that rule's replacement text or a whole-sentence rule containing the same token. Each now starts with the `s#` opener of the rule it was written for. Two rows were byte-identical copies of another row and go. --- omp-port/stale-exempt.tsv | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/omp-port/stale-exempt.tsv b/omp-port/stale-exempt.tsv index b4af722..54057ff 100644 --- a/omp-port/stale-exempt.tsv +++ b/omp-port/stale-exempt.tsv @@ -32,14 +32,12 @@ The synthesizer's quality check includes spot-verifying ci superseded: upstream there is no task `readonly` field\. Use forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. there is no task `readonly` field\.[[:space:]]*$ forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. The synthesizer returns a structured Accepted / Rejected / superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. -After all Phase B candidates complete, choose an exact dis forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. +s#After all Phase B candidates complete, choose an exact dis forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. ^- `model`: the configured `interrogate reviewers` entry, forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. `subagent_type`: `([^`]+)` forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. -`subagent_type`: `([^`]+)` forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. -agent mode \(`readonly: false`\) forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. -^- `readonly`: `true`$ forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. +s#agent mode \(`readonly: false`\) forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. s#`run_in_background: true`#one forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. -`subagent_type: "poteto-agent"` forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. +s#`subagent_type: "poteto-agent"` forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. ^Spawn one investigator per category that has a matching M superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. One subagent per PR, not batched, each a Cursor cloud agen superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. `claude-fable-[0-9.-]+(-(thinking-)?[a-z]+)?` forward-compat net: the left side is a Cursor wire token or path that upstream does not emit today. The rule earns its place by not firing, because it is the only thing standing between that text and a Cursor API in the tree. @@ -83,8 +81,8 @@ push with `--force-with-lease` only after an `ls-remote` c superseded: upstream The operator's full-autonomy grant plus the root's clean v superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. Recompute `frontier.json` from `gt` after every merge and superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. - Workers never rebase and never run `gt`\. superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. -The strict `scout` definition has no MCP grant, so never a superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. -an exact discovered full-access worker for citation spot-c superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. +s#The strict `scout` definition has no MCP grant, so never a superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. +s#an exact discovered full-access worker for citation spot-c superseded: upstream reworded this sentence and a different rule now covers the current phrasing. Kept because the reword is cheap for upstream to undo. an exact discovered (why investigators|why synthesizer|interrogate reviewers second-order net: it rewrites the output of the `model`: the `` line token rules, so raw upstream never matches it. At 4ec7ffa the whole-sentence rules for why and reflect claim the last role-line sentences first, so the token rules emit nothing for it to clean. Kept because a reworded sentence upstream falls through to those token rules, and this is what turns their role label into a capability. `readonly`: `true`#read-only posture carried in the brief forward-compat net: the Cursor wire field as it appears mid-sentence rather than as a role bullet. Upstream emits no such line today; the rule is what keeps it from reaching the tree if it ever does. \ No newline at end of file From fd6c3e46a76dd01af02099281885b9a833d6150a Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sun, 25 Oct 2026 13:01:02 +0200 Subject: [PATCH 4/4] fix(port): fail an exemption fragment that names more or fewer than one rule A stale-rule exemption is a substring of a rules.sed line, so one fragment could match several rules and keep every one of them from failing `rule staleness`. That already hid a swarm Phase B rule once. One resolver now maps each fragment to the rules it matches. `stale exemptions` fails a fragment that matches several rules or none, naming the tsv row and the rules.sed lines, and only a fragment naming exactly one rule exempts anything. The lint runs before the contracts-only exit, so the mutation harness covers it: copying the rule an exemption names must fail it. --- PORTING.md | 5 +- omp-port/check-port.sh | 85 ++++++++++++++------------------- omp-port/test-gate-mutations.sh | 18 ++++++- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/PORTING.md b/PORTING.md index 8896780..dfe06e7 100644 --- a/PORTING.md +++ b/PORTING.md @@ -82,7 +82,10 @@ consequence is a Cursor API reaching a generated skill while every other check s fix is to correct the left-hand side against what upstream actually says now, or to record the rule in `omp-port/stale-exempt.tsv` with a reason. That file is keyed on a literal fragment of the rule and never on a line number, because a line number moves the moment anyone edits a rule above it and -an exemption that silently stops applying is worse than no exemption. +an exemption that silently stops applying is worse than no exemption. `stale exemptions` fails a +fragment that matches more than one rule or none, and an ambiguous fragment exempts nothing. A +`run_in_background: true` fragment once matched a swarm Phase B sentence rule as well as the token +rule it was written for, and kept that rule from failing when it went stale. The probe's own exit status is part of the assertion for both. A probe that cannot run reports failure, not a clean result: an empty list from a failed build is not evidence that nothing is diff --git a/omp-port/check-port.sh b/omp-port/check-port.sh index ad1f4ef..1fa59ee 100755 --- a/omp-port/check-port.sh +++ b/omp-port/check-port.sh @@ -37,47 +37,34 @@ allow_count() { awk -F'\t' -v p="$2" '$0 !~ /^#/ && $2 == p' "$1" | wc -l; } # the gate then goes red for a reason nobody can reconstruct. A fragment survives every edit that # does not change the rule it names. STALE_ALLOW="${STALE_EXEMPT:-$PORT_DIR/stale-exempt.tsv}" -exempt() { - [ -f "$STALE_ALLOW" ] || return 1 - # The needle travels through the environment rather than -v, because awk applies escape - # processing to a -v assignment and a rules.sed left-hand side is full of backslashes: it would - # warn about every one of them and match the stripped text instead of the rule. - # - # Three ways a substring match can go wrong are closed here, because each one silently exempts - # more than the author intended and a wrong exemption turns the gate green, which is the exact - # failure this assertion exists to prevent: - # a whitespace-only fragment, which index() matches against almost any rule, so a stray tab - # or a double space in the file would exempt the entire table; - # a fragment too short to be distinctive, which a later rule could match by accident; - # a fragment that is only a prefix of a longer rule, which exempts that rule wholesale. - STALE_RULE="$1" awk -F'\t' ' - BEGIN { rule = ENVIRON["STALE_RULE"] } - $0 !~ /^#/ { - f = $1 - gsub(/^[[:space:]]+|[[:space:]]+$/, "", f) - # The length floor alone is not enough: a run of twenty spaces is twenty characters long - # and index() finds it in almost any rule, so a whitespace-only row exempts the table. - if (length(f) >= 20 && f ~ /[^[:space:]]/ && index(rule, f)) hit = 1 - } - END { exit !hit } - ' "$STALE_ALLOW" -} -# Every fragment has to clear the same bar the matcher enforces, so a typo is caught at review time -# rather than discovered as an exemption that quietly stopped applying. -stale_exempt_bad() { +# Resolves each exemption to the one rule it names, printing `ok` or +# `bad` per entry, so the lint and the staleness check read one answer. Only a fragment +# that names exactly one rule exempts anything. A blank or short fragment is found by index() in +# almost any rule, and a fragment that matches two rules lets the one it was not written for go +# stale in silence: a `run_in_background: true` fragment once hid a swarm Phase B rule that way. +# Only a genuinely empty line is not an entry; a line holding whitespace is one, and fails. +stale_exempt_resolve() { [ -f "$STALE_ALLOW" ] || return 0 awk -F'\t' ' - $0 !~ /^#/ { - # Only a genuinely empty line is not an entry. A line holding whitespace is one, and is - # caught below: that is the shape index() would match against every rule, so skipping it - # here would reinstate the exact hole this lint exists to close. - if ($0 == "") next + FILENAME == ARGV[1] { + if ($0 ~ /^[0-9]+\t/) { n++; at[n] = $1; rule[n] = substr($0, length($1) + 2) } + else rule[n] = rule[n] "\n" $0 + next + } + $0 !~ /^#/ && $0 != "" { f = $1 gsub(/^[[:space:]]+|[[:space:]]+$/, "", f) - if (f !~ /[^[:space:]]/ || length(f) < 20) - printf " rules.sed exemption fragment is blank or under 20 chars: [%s]\n", f + if (f !~ /[^[:space:]]/ || length(f) < 20) { + printf "bad\tstale-exempt.tsv:%d fragment is blank or under 20 chars: [%s]\n", FNR, f + next + } + c = 0; hits = "" + for (i = 1; i <= n; i++) if (index(rule[i], f)) hits = hits (c++ ? ", " : "") at[i] + if (c == 1) printf "ok\t%s\n", hits + else if (c == 0) printf "bad\tstale-exempt.tsv:%d fragment matches no rule: [%s]\n", FNR, f + else printf "bad\tstale-exempt.tsv:%d fragment matches %d rules, rules.sed lines %s: [%s]\n", FNR, c, hits, f } - ' "$STALE_ALLOW" + ' <(rule_records) "$STALE_ALLOW" } # The gate patterns live in omp-port/lib.sh, one source shared with the sync script. pattern_for() { @@ -359,6 +346,19 @@ else report "guide claims" "PASS ${#guide_skills[@]} skills, prototype and multi-phase-plan playbooks, check-plan.mjs, Feature Map, pin" fi +# Linted on every run, before the contracts-only exit, so a bad exemption fails the day it is +# written whether or not any rule is stale yet, and the fixture mutations can reach it. +exemptions=$(stale_exempt_resolve) +exempt_lines=$(awk -F'\t' '$1 == "ok" { print $2 }' <<<"$exemptions") +bad_frag=$(awk -F'\t' '$1 == "bad" { print $2 }' <<<"$exemptions") +if [ -n "$bad_frag" ]; then + report "stale exemptions" "FAIL" + while read -r l; do [ -n "$l" ] && violate "$l"; done <<<"$bad_frag" + violate "an exemption must name exactly one rules.sed rule by a fragment of 20 or more characters, and any other fragment exempts nothing; starting it with the rule's s# opener keeps it off other rules' replacement text" +else + report "stale exemptions" "PASS $(grep -c . <<<"$exempt_lines") fragment(s), each naming exactly one rule" +fi + if [ "${CHECK_PORT_CONTRACTS_ONLY:-}" = 1 ]; then echo if [ "$fail" -eq 0 ]; then echo "check-port contracts: PASS"; else echo "check-port contracts: FAIL"; fi @@ -726,7 +726,7 @@ if [ "$canon_ok" = yes ]; then unexempted="" while IFS=$'\t' read -r n rule; do [ -n "$n" ] || continue - exempt "$rule" || unexempted="${unexempted}rules.sed:${n}: ${rule}"$'\n' + grep -qxF "$n" <<<"$exempt_lines" || unexempted="${unexempted}rules.sed:${n}: ${rule}"$'\n' done <<<"$stale" total=$(grep -c . <<<"$stale" || true) if [ -n "$unexempted" ]; then @@ -746,17 +746,6 @@ else report "rule staleness" "SKIP needs the clone at $CANON" fi -# A fragment that is blank or shorter than the matcher requires exempts nothing at all, which reads -# like a working exemption and is not one. Linted separately so the mistake surfaces whether or not -# any rule is currently stale. -bad_frag=$(stale_exempt_bad) -if [ -n "$bad_frag" ]; then - report "stale exemptions" "FAIL" - while read -r l; do [ -n "$l" ] && violate "$l"; done <<<"$bad_frag" - violate "an exemption fragment that is blank or under 20 characters exempts nothing, so the rule it was written for will fail for the wrong reason" -else - report "stale exemptions" "PASS $(grep -cvE '^[[:space:]]*(#|$)' "$STALE_ALLOW" 2>/dev/null || echo 0) fragment(s), all usable" -fi rm -rf "$scratch" "$buildlog" echo diff --git a/omp-port/test-gate-mutations.sh b/omp-port/test-gate-mutations.sh index 1e9e0ec..04e928c 100755 --- a/omp-port/test-gate-mutations.sh +++ b/omp-port/test-gate-mutations.sh @@ -4,17 +4,20 @@ set -uo pipefail PORT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P) REPO_ROOT=$(cd "$PORT_DIR/.." && pwd -P) FIXTURE=$(mktemp -d) -trap 'rm -rf "$FIXTURE"' EXIT +PORT_FIXTURE=$(mktemp -d) +trap 'rm -rf "$FIXTURE" "$PORT_FIXTURE"' EXIT fail=0 reset_fixture() { rm -rf "$FIXTURE" mkdir -p "$FIXTURE" cp -a "$REPO_ROOT/plugins/pstack/." "$FIXTURE/" + cp "$PORT_DIR/rules.sed" "$PORT_DIR/stale-exempt.tsv" "$PORT_FIXTURE/" } run_contracts() { - CHECK_PORT_ROOT="$FIXTURE" CHECK_PORT_CONTRACTS_ONLY=1 bash "$PORT_DIR/check-port.sh" + CHECK_PORT_ROOT="$FIXTURE" RULES="$PORT_FIXTURE/rules.sed" STALE_EXEMPT="$PORT_FIXTURE/stale-exempt.tsv" \ + CHECK_PORT_CONTRACTS_ONLY=1 bash "$PORT_DIR/check-port.sh" } reset_fixture @@ -133,6 +136,17 @@ mutate_veto_provenance() { expect_failure 'external veto provenance' 'external capability provenance' mutate_veto_provenance +# A fragment written for one stale rule once matched a second rule too and kept it from failing the +# gate. Copying the rule the first exemption names gives that fragment two rules to match. +mutate_shared_fragment() { + local frag rule + frag=$(awk -F'\t' '$0 !~ /^#/ && $0 != "" { print $1; exit }' "$PORT_FIXTURE/stale-exempt.tsv") + rule=$(grep -m1 -F -- "$frag" "$PORT_FIXTURE/rules.sed") + printf '%s\n' "$rule" >>"$PORT_FIXTURE/rules.sed" +} + +expect_failure 'shared exemption fragment' 'stale exemptions' mutate_shared_fragment + if [ "$fail" -ne 0 ]; then printf 'mutation tests: FAIL\n'