From d4d83338c1bef148f4f40c3150aa54567c24688f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 05:12:33 +0000 Subject: [PATCH 01/10] =?UTF-8?q?feat(engineering):=20add=20human-gate=20?= =?UTF-8?q?=E2=80=94=20batched=20human=20review=20as=20a=20verification=20?= =?UTF-8?q?artifact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audits petergyang/human-review and ships a conceptual derivation that fits this repo's stdlib-only conventions. Audit (audit/human-review-2026-08/AUDIT.md): upstream is a well-engineered ~5,200 LOC Node app — its own test suite passes 90/90, and its security model (loopback bind, DNS-rebinding Host check, constant-time token compare, realpath traversal guard, inert Markdown renderer, 45-min idle shutdown) is better than most local-server tools. It still does not fit: Node 20 + an npm runtime dependency fails the same stdlib-only test that kept the heavier skillopt package out in v2.11.2. Seven findings, three material — F1 (HIGH) unpinned `npx -y` executes a newly published version on every run; F2 (MED) "do not end your turn" plus re-poll on timeout with no headless guard or retry cap; F3 (MED) only /api/* is token-gated. Also: despite the name it is not a humanizer. This is human approval, not human voice — no overlap with behuman or content-humanizer. New plugin engineering/human-gate, three stdlib scripts, no server or socket: - review_page_builder.py — Markdown/HTML to a single-file anchored review page with zero network requests (~11 KB, opens over file://). Escapes before applying inline markup, scheme-allowlists hrefs, drops script/style on HTML input. - feedback_parser.py — sidecar to batch.v1 JSON. BLOCKER/MAJOR/MINOR/NIT (matching md-review) plus EDIT/NOTE/APPROVE. Verifies quotes against the real file; strips HTML comments so a documented example cannot parse as a real sign-off. - human_gate.py — open/status/collect/close/reset with atomic writes and 0700/0600 state. Rules G1-G6 refuse to close on: no collected round, an open BLOCKER/MAJOR, an unnamed reviewer, a sidecar changed after collection, an exhausted round cap (exit 5 = escalate), or an undocumented waiver. Loop discipline deliberately inverts upstream: no blocking poll, a headless guard, a round cap that escalates. The sidecar is hand-writable Markdown, so the loop closes over SSH and in CI. The optional bridge to upstream is opt-in and always version-pinned. Adds 3 references (7-8 sources each), a batch.v1 schema, a worked example, cs-human-gate agent, /cs:human-gate command. SKILL.md passes the write-a-skill 6-item checklist 6/6; description validator PASS. Counters: skills 362->363, tools 644->647, refs 741->744, agents 102->103, commands 116->117, plugins 88->89. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01233Eggb2cjSYf96X6C3pCm --- .claude-plugin/marketplace.json | 22 +- CHANGELOG.md | 74 ++ CLAUDE.md | 4 +- README.md | 20 +- audit/human-review-2026-08/AUDIT.md | 179 +++++ .../human-gate/.claude-plugin/plugin.json | 25 + engineering/human-gate/README.md | 143 ++++ .../human-gate/agents/cs-human-gate.md | 104 +++ .../human-gate/commands/cs-human-gate.md | 124 ++++ .../human-gate/skills/human-gate/SKILL.md | 99 +++ .../human-gate/assets/batch.v1.schema.json | 89 +++ .../assets/example_review_sidecar.md | 55 ++ .../references/feedback_batching.md | 163 +++++ .../references/human_in_the_loop_canon.md | 134 ++++ .../references/review_loop_discipline.md | 170 +++++ .../human-gate/scripts/feedback_parser.py | 431 +++++++++++ .../skills/human-gate/scripts/human_gate.py | 521 ++++++++++++++ .../human-gate/scripts/review_page_builder.py | 678 ++++++++++++++++++ 18 files changed, 3022 insertions(+), 13 deletions(-) create mode 100644 audit/human-review-2026-08/AUDIT.md create mode 100644 engineering/human-gate/.claude-plugin/plugin.json create mode 100644 engineering/human-gate/README.md create mode 100644 engineering/human-gate/agents/cs-human-gate.md create mode 100644 engineering/human-gate/commands/cs-human-gate.md create mode 100644 engineering/human-gate/skills/human-gate/SKILL.md create mode 100644 engineering/human-gate/skills/human-gate/assets/batch.v1.schema.json create mode 100644 engineering/human-gate/skills/human-gate/assets/example_review_sidecar.md create mode 100644 engineering/human-gate/skills/human-gate/references/feedback_batching.md create mode 100644 engineering/human-gate/skills/human-gate/references/human_in_the_loop_canon.md create mode 100644 engineering/human-gate/skills/human-gate/references/review_loop_discipline.md create mode 100644 engineering/human-gate/skills/human-gate/scripts/feedback_parser.py create mode 100644 engineering/human-gate/skills/human-gate/scripts/human_gate.py create mode 100644 engineering/human-gate/skills/human-gate/scripts/review_page_builder.py diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index cb9b55c9..a9913c80 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "homepage": "https://github.com/alirezarezvani/claude-skills", "repository": "https://github.com/alirezarezvani/claude-skills", "metadata": { - "description": "362 production-ready skills across 18 domains (engineering, engineering-core, marketing, product, c-level, compliance-os, project management, RA/QM, business growth, finance, productivity, marketing top-level, research, research-ops, business-operations, commercial, markdown-html, loop-library, plus standards). 644 Python tools, 741 reference guides, 102 agents (cs-* + personas), 116 slash commands across 88 marketplace plugins. v2.11.2 vendors engineering/skillopt-sleep — a verbatim copy of microsoft/SkillOpt's stdlib-only skillopt_sleep engine + Claude Code plugin surface, giving a local agent a nightly gated self-improvement cycle (read-only session harvest -> mine -> offline replay -> held-out-gated CLAUDE.md/SKILL.md edits -> staged for explicit /skillopt-sleep adopt). productivity/fable-goal (unreleased, post-v2.11.1) converts a rambling description of a desired outcome into one polished /goal prompt for a fresh autonomous session. v2.11.1 turns product-team and project-management into agent-harness domains: fork-orchestrators with deterministic goal routers, a Jira MCP snapshot bridge (Kanban flow metrics + Monte Carlo forecasting), a delegation-governance loop gate, a continuous-discovery cadence tracker, and an Opportunity Solution Tree linter, with /cs:pm and /cs:product command families. v2.10.3 completes the markdown-html domain with md-slides — slide-deck converter (arrow-key / Space / PgDn / Home/End / P keyboard navigation + presenter mode with split-view clock + speaker notes + next-slide preview + URL-hash deep linking like #3 for direct slide jumps + @media print page-per-slide for browser-native PDF export). Reuses md-document's markdown parser; vanilla JS only (no framework runtime); Prism.js opt-in via --syntax. Joins md-review (v2.10.2 code-review converter), md-document (v2.10.1 long-form converter), and the v2.10.0 foundation (orchestrator + design-system). Compatible with Claude Code, Codex CLI, Gemini CLI, Cursor, OpenClaw, Hermes Agent, Mistral Vibe, and 5 more coding agents.", + "description": "363 production-ready skills across 18 domains (engineering, engineering-core, marketing, product, c-level, compliance-os, project management, RA/QM, business growth, finance, productivity, marketing top-level, research, research-ops, business-operations, commercial, markdown-html, loop-library, plus standards). 647 Python tools, 744 reference guides, 103 agents (cs-* + personas), 117 slash commands across 89 marketplace plugins. v2.11.2 vendors engineering/skillopt-sleep — a verbatim copy of microsoft/SkillOpt's stdlib-only skillopt_sleep engine + Claude Code plugin surface, giving a local agent a nightly gated self-improvement cycle (read-only session harvest -> mine -> offline replay -> held-out-gated CLAUDE.md/SKILL.md edits -> staged for explicit /skillopt-sleep adopt). productivity/fable-goal (unreleased, post-v2.11.1) converts a rambling description of a desired outcome into one polished /goal prompt for a fresh autonomous session. v2.11.1 turns product-team and project-management into agent-harness domains: fork-orchestrators with deterministic goal routers, a Jira MCP snapshot bridge (Kanban flow metrics + Monte Carlo forecasting), a delegation-governance loop gate, a continuous-discovery cadence tracker, and an Opportunity Solution Tree linter, with /cs:pm and /cs:product command families. v2.10.3 completes the markdown-html domain with md-slides — slide-deck converter (arrow-key / Space / PgDn / Home/End / P keyboard navigation + presenter mode with split-view clock + speaker notes + next-slide preview + URL-hash deep linking like #3 for direct slide jumps + @media print page-per-slide for browser-native PDF export). Reuses md-document's markdown parser; vanilla JS only (no framework runtime); Prism.js opt-in via --syntax. Joins md-review (v2.10.2 code-review converter), md-document (v2.10.1 long-form converter), and the v2.10.0 foundation (orchestrator + design-system). Compatible with Claude Code, Codex CLI, Gemini CLI, Cursor, OpenClaw, Hermes Agent, Mistral Vibe, and 5 more coding agents.", "version": "2.11.2" }, "plugins": [ @@ -1862,6 +1862,26 @@ "collab-proof" ], "category": "engineering" + }, + { + "name": "human-gate", + "source": "./engineering/human-gate", + "description": "Human-verification gate for an agent loop: builds a single-file review page (zero network requests), collects batched feedback as structured batch.v1 data instead of chat prose, and refuses to close while a BLOCKER is open, the reviewer is unnamed, or nobody has reviewed. Non-blocking, headless-guarded, round-capped. Stdlib-only.", + "version": "1.0.0", + "author": { + "name": "Alireza Rezvani", + "url": "https://alirezarezvani.com" + }, + "keywords": [ + "human-in-the-loop", + "review-gate", + "sign-off", + "agent-loop", + "verification", + "batched-feedback", + "human-gate" + ], + "category": "engineering" } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 2573f773..a31a2206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,80 @@ All notable changes to the Claude Skills Library will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] — human-gate: batched human review as a verification artifact (this PR) + +### Audited — `petergyang/human-review` + +Public audit record at `audit/human-review-2026-08/AUDIT.md`. Upstream (npm +`human-review@0.6.0`, MIT © Peter Yang) is a ~5,200 LOC Node application that opens +an HTML/Markdown file or localhost page in the browser for direct editing and +anchored comments, then ships the batch back to the agent as JSON. **Verified: its +own test suite passes 90/90.** Security posture is better than most local-server +tools — loopback-only bind, DNS-rebinding `Host` check, constant-time token compare, +realpath-checked traversal guard, a deliberately inert Markdown renderer, and a +45-minute idle self-shutdown. + +**Verdict: do not vendor, do adopt the pattern.** Node 20 + an npm runtime dependency +fails the same stdlib-only test that kept the heavier `skillopt` package out in +v2.11.2. Seven findings recorded, three material: **F1 (HIGH)** the skill instructs +the agent to run unpinned `npx -y human-review`, so every invocation may fetch and +execute a newly published version; **F2 (MED)** "do not end your turn" plus re-poll +on timeout, with no headless guard and no retry cap — the AR5 loop-discipline gap +`audit/engineering-agentic-2026-07/` already named as repo-wide; **F3 (MED)** only +`/api/*` is token-gated, not `/artifact/` or `/s/`. + +Also worth stating plainly: despite the name, this is **not** a humanizer. It is +human *approval*, not human *voice* — no overlap with `engineering/behuman` or +`marketing-skill/content-humanizer`. + +### Added — `engineering/human-gate` + +Conceptual derivation (no upstream code copied), built to this repo's conventions: +three stdlib-only Python scripts, no server, no socket, no network fetch. + +- **`review_page_builder.py`** — Markdown/HTML → single-file review page with every + block anchored (`data-hg="b7"`). **Zero network requests** — no CDN, no fonts, no + Prism; ~11 KB, opens over `file://`. Markdown is rendered by a stdlib subset parser + that escapes before applying inline markup and scheme-allowlists every href; + HTML input is re-emitted through `html.parser` with ` + + +""" + + +def build_page(source_text, target_name, sidecar_name, is_markdown, round_number=1): + content, blocks = build_content(source_text, is_markdown) + if not blocks: + return None, [] + config = { + "target": target_name, + "sidecar": sidecar_name, + "round": round_number, + "blocks": blocks, + } + page = PAGE.replace("__CONTENT__", content) + page = page.replace("__TITLE__", html.escape(target_name, quote=False)) + # json.dumps output is embedded in a " + page = page.replace( + "__CONFIG__", json.dumps(config).replace(".review.md)" + ) + parser.add_argument( + "--launch", action="store_true", help="open the page in the default browser" + ) + parser.add_argument( + "--sample", action="store_true", help="build a page from a built-in sample" + ) + args = parser.parse_args(argv) + + if args.sample: + source, name = SAMPLE_MD, "quarterly-plan.md" + is_md = True + out_path = args.output or "quarterly-plan.review.html" + elif args.artifact: + try: + with open(args.artifact, "r", encoding="utf-8") as handle: + source = handle.read() + except OSError as err: + parser.error("cannot read %s: %s" % (args.artifact, err)) + return 1 + name = os.path.basename(args.artifact) + is_md = os.path.splitext(name)[1].lower() in (".md", ".markdown") + base = os.path.splitext(args.artifact)[0] + out_path = args.output or (base + ".review.html") + else: + parser.error("provide an artifact path or --sample") + return 1 + + sidecar = args.sidecar or (os.path.splitext(name)[0] + ".review.md") + page, blocks = build_page(source, name, sidecar, is_md, args.round) + if page is None: + sys.stderr.write("No reviewable blocks found in %s\n" % name) + return 2 + + try: + with open(out_path, "w", encoding="utf-8") as handle: + handle.write(page) + except OSError as err: + parser.error("cannot write %s: %s" % (out_path, err)) + return 1 + + size_kb = len(page.encode("utf-8")) / 1024.0 + print("Review page: %s" % out_path) + print("Blocks: %d" % len(blocks)) + print("Size: %.1f KB (single file, no network requests)" % size_kb) + print("Sidecar: %s <- export lands here, or write it by hand" % sidecar) + + if args.launch: + import webbrowser + + webbrowser.open("file://" + os.path.abspath(out_path)) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From daa4dde7d134f93711023a78ca3970ed1069a8b3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 05:32:28 +0000 Subject: [PATCH 02/10] fix(human-gate): add G7 integrity rule; drop stray generated artifact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both from PR review on #948, both reproduced before fixing. G7 — a real hole in the skill's core promise. feedback_parser downgrades an unrecognised severity heading to NIT and records it only as advisory prose in `problems`, which cmd_close never read. Reproduced: a sidecar with `## BLOKCER` carrying "No source. Do not ship this." collected as a NIT, and close exited 0 — a reviewer's genuine blocker lost to a typo. Same gap covered EDIT items with no `+ after:` line and quotes that do not appear in the target file. close now refuses (exit 2) while the last collected round carries unresolved integrity problems. Problems that already have their own rule are filtered via GATED_ELSEWHERE so G2/G3 are not double-reported. Verified: typo'd severity, missing EDIT replacement, and quote-not-in-file each refuse; a clean sidecar still passes; a missing reviewer still reports G3 alone. Stray artifact — quarterly-plan.review.html was committed at the repo root. It came from a `review_page_builder.py --sample` run during the post-merge verification sweep with cwd at the repo root, then got swept up by `git add -A`. Removed, and .gitignore now covers `*.review.html` + `.human-gate/` so neither this repo nor a user of the skill re-commits a disposable review page. The sidecar (.review.md) is deliberately NOT ignored — that is the reviewer's feedback and belongs in git. G7 documented in the script docstring, SKILL.md, README, the command, plugin.json (description + derivation_note) and CHANGELOG. Not acted on: the reviewer's note that cmd_status's success line is terse — they flagged it as "not a real issue" and the JSON branch already carries blocking_open. Re-verified: derive_counters --check passes, check_plugin_json --all 90/90, write-a-skill checklist 6/6 PASS, description validator PASS, all three scripts --help/--sample green. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01233Eggb2cjSYf96X6C3pCm --- .gitignore | 7 + CHANGELOG.md | 16 +- .../human-gate/.claude-plugin/plugin.json | 4 +- engineering/human-gate/README.md | 1 + .../human-gate/commands/cs-human-gate.md | 1 + .../human-gate/skills/human-gate/SKILL.md | 1 + .../skills/human-gate/scripts/human_gate.py | 17 ++ quarterly-plan.review.html | 266 ------------------ 8 files changed, 41 insertions(+), 272 deletions(-) delete mode 100644 quarterly-plan.review.html diff --git a/.gitignore b/.gitignore index 3377bf9e..43c0e378 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,10 @@ tests/ # Autoresearch agent workspace .autoresearch/ .idea/ + +# human-gate generated artifacts (review pages + local gate state). +# The review page is a disposable viewing surface rebuilt on every `open`; +# the sidecar (.review.md) is deliberately NOT ignored — it is the +# reviewer's feedback and belongs next to the artifact in git. +*.review.html +.human-gate/ diff --git a/CHANGELOG.md b/CHANGELOG.md index cb0dda2e..60268a2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,11 +49,19 @@ three stdlib-only Python scripts, no server, no socket, no network fetch. file and reports mismatches rather than swallowing them. Strips HTML comments so an example written inside one cannot parse as a real sign-off. - **`human_gate.py`** — `open`/`status`/`collect`/`close`/`reset` state machine with - atomic writes and `0700`/`0600` state permissions. Gate rules **G1–G6**: refuses to + atomic writes and `0700`/`0600` state permissions. Gate rules **G1–G7**: refuses to close with no collected round, an open BLOCKER/MAJOR, an unnamed reviewer, a sidecar - changed after collection, an exhausted round cap (exit 5 = escalate, never pass), or - a waiver without a recorded reason. Waivers store both the reason and every refusal - they overrode. + changed after collection, an exhausted round cap (exit 5 = escalate, never pass), a + waiver without a recorded reason, or a round carrying unresolved integrity problems. + Waivers store both the reason and every refusal they overrode. + +**G7 came out of PR review** and closes a real hole: a mistyped severity heading +(`## BLOKCER`) silently downgrades to `NIT`, so before this a reviewer's genuine blocker +could be lost to a typo and `close` would still exit 0. Reproduced, then fixed — the +parser's integrity problems (unknown severity, EDIT with no replacement text, a quote +that is not in the target file) are now closer-blocking rather than advisory prose. +Problems that already have their own rule (G2, G3) are filtered so they are not +double-reported. **Loop discipline — the deliberate inversion of upstream.** There is no blocking poll: `status` returns immediately, `open` detects a headless host (`CI`, SSH, no `DISPLAY`) diff --git a/engineering/human-gate/.claude-plugin/plugin.json b/engineering/human-gate/.claude-plugin/plugin.json index ee2edf4d..fb399c91 100644 --- a/engineering/human-gate/.claude-plugin/plugin.json +++ b/engineering/human-gate/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "human-gate", - "description": "The human-verification lane for an agent loop. Builds a single-file HTML review page for a Markdown or HTML artifact (zero network requests, opens over file://), collects batched feedback as a structured batch.v1 artifact instead of chat prose, and runs a gate that refuses to close while a BLOCKER is open, the reviewer is unnamed, the sidecar changed after collection, or nobody has reviewed at all. Non-blocking by design: no poll loop, a headless guard, a round cap, and escalation on exhaustion. Feedback lands in a hand-writable Markdown sidecar, so the loop still works over SSH and in CI where no browser exists. Use before shipping a plan, spec, RFC, report, or any irreversible action, or when the user says 'let me review that', 'get sign-off', or 'don't ship until I've seen it'. The human counterpart to agent-harness's machine verification -- NOT a content humanizer (see content-humanizer / behuman).", + "description": "The human-verification lane for an agent loop. Builds a single-file HTML review page for a Markdown or HTML artifact (zero network requests, opens over file://), collects batched feedback as a structured batch.v1 artifact instead of chat prose, and runs a gate that refuses to close while a BLOCKER is open, the reviewer is unnamed, the sidecar changed after collection, the round carries unresolved integrity problems (a mistyped severity silently downgrades to NIT, so a real blocker can be lost to a typo), or nobody has reviewed at all. Non-blocking by design: no poll loop, a headless guard, a round cap, and escalation on exhaustion. Feedback lands in a hand-writable Markdown sidecar, so the loop still works over SSH and in CI where no browser exists. Use before shipping a plan, spec, RFC, report, or any irreversible action, or when the user says 'let me review that', 'get sign-off', or 'don't ship until I've seen it'. The human counterpart to agent-harness's machine verification -- NOT a content humanizer (see content-humanizer / behuman).", "version": "1.0.0", "author": { "name": "Alireza Rezvani", @@ -20,6 +20,6 @@ "upstream": "https://github.com/petergyang/human-review", "upstream_author": "Peter Yang", "upstream_license": "MIT", - "derivation_note": "CONCEPTUAL derivation -- no upstream source code is copied or vendored. Upstream is a ~5,200 LOC Node application (Node >= 20, npm dependency 'marked', detached local HTTP server, browser chrome UI); this repo's conventions are stdlib-Python-only with no build system and no dependencies, which is the same test that kept the heavier skillopt package out in v2.11.2. What was taken is the pattern: human feedback as a batched, anchored, machine-parseable artifact rather than chat prose, and the verbatim-edit rule (a reviewer's `after` text is carried across exactly, applied to the generating source as well as the rendered artifact). What was deliberately built differently, and why, is recorded per-finding in audit/human-review-2026-08/AUDIT.md: no unpinned `npx -y` execution (F1 -- upstream instructs the agent to auto-fetch and run the latest published version on every invocation); no blocking poll and an explicit headless guard plus round cap with escalation (F2 -- upstream tells the agent not to end its turn and to re-poll indefinitely on timeout, with no terminating condition on a browserless host); no local HTTP server or socket at all, so the partially-ungated-route question does not arise (F3); no writes to user-global agent config as an install side effect (F4); no silent in-place overwrite of the reviewed file (F5); no third-party parser in the trust path (F6). Added beyond upstream: a closing gate (G1-G6) that refuses to report done without a collected round, a named reviewer, and resolved blocking items, with explicit recorded waivers. The optional bridge documented in SKILL.md invokes upstream PINNED (`npx -y human-review@0.6.0`) and is opt-in only; the gate still governs closure. Upstream's own test suite was run during the audit: 90/90 passing." + "derivation_note": "CONCEPTUAL derivation -- no upstream source code is copied or vendored. Upstream is a ~5,200 LOC Node application (Node >= 20, npm dependency 'marked', detached local HTTP server, browser chrome UI); this repo's conventions are stdlib-Python-only with no build system and no dependencies, which is the same test that kept the heavier skillopt package out in v2.11.2. What was taken is the pattern: human feedback as a batched, anchored, machine-parseable artifact rather than chat prose, and the verbatim-edit rule (a reviewer's `after` text is carried across exactly, applied to the generating source as well as the rendered artifact). What was deliberately built differently, and why, is recorded per-finding in audit/human-review-2026-08/AUDIT.md: no unpinned `npx -y` execution (F1 -- upstream instructs the agent to auto-fetch and run the latest published version on every invocation); no blocking poll and an explicit headless guard plus round cap with escalation (F2 -- upstream tells the agent not to end its turn and to re-poll indefinitely on timeout, with no terminating condition on a browserless host); no local HTTP server or socket at all, so the partially-ungated-route question does not arise (F3); no writes to user-global agent config as an install side effect (F4); no silent in-place overwrite of the reviewed file (F5); no third-party parser in the trust path (F6). Added beyond upstream: a closing gate (G1-G7) that refuses to report done without a collected round, a named reviewer, and resolved blocking items, with explicit recorded waivers. G7 was added during PR review after a reproduced hole: a mistyped severity heading (`## BLOKCER`) silently downgrades to NIT, so a reviewer's genuine blocker could be lost to a typo while close still exited 0; the parser's integrity problems are now closer-blocking rather than advisory prose. The optional bridge documented in SKILL.md invokes upstream PINNED (`npx -y human-review@0.6.0`) and is opt-in only; the gate still governs closure. Upstream's own test suite was run during the audit: 90/90 passing." } } diff --git a/engineering/human-gate/README.md b/engineering/human-gate/README.md index 0575d92d..31bc15fd 100644 --- a/engineering/human-gate/README.md +++ b/engineering/human-gate/README.md @@ -90,6 +90,7 @@ Severities are **BLOCKER / MAJOR / MINOR / NIT** — the same ladder | **G4** | the sidecar changed after the last collect | | **G5** | round cap exhausted → **escalate**, never pass | | **G6** | a waiver is used without a recorded reason | +| **G7** | the round carries unresolved integrity problems — a mistyped severity (`## BLOKCER`) silently downgrades to NIT, an EDIT has no replacement text, or a quote is not in the file | Overrides are legitimate and must be explicit: diff --git a/engineering/human-gate/commands/cs-human-gate.md b/engineering/human-gate/commands/cs-human-gate.md index ff31d228..4cb7f86f 100644 --- a/engineering/human-gate/commands/cs-human-gate.md +++ b/engineering/human-gate/commands/cs-human-gate.md @@ -93,6 +93,7 @@ python3 $S/human_gate.py close "$ARTIFACT" | G4 | the sidecar changed after the last collect | | G5 | round cap exhausted → escalate | | G6 | waiver used without a recorded reason | +| G7 | the round carries unresolved integrity problems (mistyped severity, EDIT with no replacement, quote not in the file) | **Exit 2 means you are not done.** Report what is open, not a summary that implies success. diff --git a/engineering/human-gate/skills/human-gate/SKILL.md b/engineering/human-gate/skills/human-gate/SKILL.md index 3b912b24..3119d86c 100644 --- a/engineering/human-gate/skills/human-gate/SKILL.md +++ b/engineering/human-gate/skills/human-gate/SKILL.md @@ -62,6 +62,7 @@ already wrote, as `- before:` / `+ after:` lines. | **G1** | no round collected | **G4** | sidecar changed after the last collect | | **G2** | a BLOCKER or MAJOR is open | **G5** | round cap exhausted → **escalate**, never pass | | **G3** | no named reviewer | **G6** | waiver used without a recorded reason | +| **G7** | the round carries unresolved integrity problems — a mistyped severity silently downgrades to NIT, so a real blocker can be lost to a typo | | | Overrides are legitimate, and must be explicit: `close plan.md --waive ""`. diff --git a/engineering/human-gate/skills/human-gate/scripts/human_gate.py b/engineering/human-gate/skills/human-gate/scripts/human_gate.py index c7b7df73..19b4fdaa 100644 --- a/engineering/human-gate/skills/human-gate/scripts/human_gate.py +++ b/engineering/human-gate/skills/human-gate/scripts/human_gate.py @@ -22,6 +22,9 @@ Gate rules G5 rounds are capped (--max-rounds); exhaustion escalates, it never silently keeps looping G6 a waiver is allowed but must be explicit, reasoned, and recorded + G7 close refuses while the last round carries unresolved integrity + problems (mistyped severity, EDIT with no replacement, a quote not + in the file) — those downgrade silently, so prose is not enough Loop discipline --------------- @@ -54,6 +57,15 @@ HERE = os.path.dirname(os.path.abspath(__file__)) BLOCKING = {"BLOCKER", "MAJOR"} DEFAULT_MAX_ROUNDS = 5 +# Parser problems that already have their own gate rule. Everything else the +# parser reports is an integrity problem the gate must refuse on (G7) — a +# mistyped severity silently downgrades to NIT, so without this a reviewer's +# real blocker can be lost to a typo and close would still pass. +GATED_ELSEWHERE = ( + "no 'reviewer:' line", # G3 + "APPROVE is present alongside", # G2 +) + def _load_parser_module(): import importlib.util @@ -340,6 +352,11 @@ def cmd_close(args): "collect again before closing" % last["round"] ) + for problem in last.get("problems", []): + if problem.startswith(GATED_ELSEWHERE): + continue + refusals.append("G7 round %d integrity: %s" % (last["round"], problem)) + if refusals and args.waive: state["waiver"] = { "reason": args.waive, diff --git a/quarterly-plan.review.html b/quarterly-plan.review.html deleted file mode 100644 index 5cfd7568..00000000 --- a/quarterly-plan.review.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - -Review — quarterly-plan.md - - - -
-

Review: quarterly-plan.md

- no feedback yet - - - - - -
-
-

Quarterly plan

-

We expect a 40% lift in activation.

-

Bets

-
  • Ship the onboarding rewrite
  • Retire the legacy importer
-

Risks

-

The team will endeavour to deliver incremental value.

- -
-
- - - From 70c908a65a9df27c9f7a4fc767891b7b79c16a6c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 05:46:07 +0000 Subject: [PATCH 03/10] fix(human-gate): sanitize reviewed HTML; fix 3 gate-integrity defects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second PR-review round on #948. All four reproduced before fixing. 1. HIGH — reviewed HTML executed in the review page. BlockTagger re-emitted attributes verbatim, escaping values but never filtering attribute names or URL schemes. The Markdown path has had _safe_href scheme-allowlisting all along; the HTML path had nothing. Reproduced: a draft.html containing ``, `` and an