Skip to content

Commit 2f9276b

Browse files
v2.0.0 -- hook-free execution: delete the hook registrations, replace them with explicit commands and MCP tools (#255)
Deletes the PreToolUse, PostToolUse and UserPromptSubmit hook registrations and replaces them with six explicit slash commands and user-scope MCP tools. Stop and Notification are retained byte-identical. Merged with CI's test job red on ubuntu-latest and green on Windows, as an informed accepted risk. The two workflows this sprint added — plugin conformance and push-gate reachability — both pass on Linux, including the assertion that the MCP push gate is reachable where the hook no longer is. See docs/REVIEW-INDEX.md entries 52 and 53. 36 of 37 sprint issues closed; #259 deliberately open because the NFR-1 measurement has never produced a pass.</commit_message>
1 parent c1f8cfd commit 2f9276b

432 files changed

Lines changed: 78589 additions & 2735 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude-plugin/marketplace.json‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "techdeveloper-org",
3+
"owner": {
4+
"name": "techdeveloper-org"
5+
},
6+
"description": "Private marketplace catalog for the techdeveloper-org Claude Code plugins.",
7+
"plugins": [
8+
{
9+
"name": "claude-workflow-engine",
10+
"source": "./plugin",
11+
"description": "SDLC orchestration entry points for the Claude Workflow Engine. Ships commands, agents and skills only: zero hooks and zero bundled MCP servers, so an enabled-but-uninvoked plugin contributes no processes to a session."
12+
}
13+
]
14+
}

‎.github/workflows/ci.yml‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,57 @@ jobs:
125125
- name: Secrets scan (gate)
126126
run: python scripts/secrets_check.py
127127

128+
# --- Policy audit conformance (hard gates) -----------------------------
129+
# Both are stdlib-only, so they run before dependency install.
130+
# Until this step existed, nothing in the repository invoked either gate:
131+
# they passed locally and were never executed against the artifact in CI.
132+
# A check nothing runs is the same class of defect as a check that
133+
# cannot fail.
134+
#
135+
# The matrix gate asserts AC1-AC7 for V2-004 and V2-005, including that
136+
# every Post-plugin plan cell is populated and in vocabulary. It is
137+
# EXPECTED to fail whenever a disposition is left undecided, and that
138+
# failure is the mechanism that surfaces the decision rather than a
139+
# broken check to be loosened.
140+
- name: Policy audit matrix conformance (gate)
141+
run: python scripts/verify_policy_audit_matrix.py
142+
143+
# The orphan gate asserts an empty symmetric difference between the
144+
# matrix and as-built-prd section 4.2, in both directions. It is not a
145+
# count check: a count agrees whenever two names are simultaneously
146+
# wrong.
147+
- name: Policy orphan dispositions (gate)
148+
run: python scripts/verify_policy_orphan_dispositions.py
149+
150+
# SRS FR-30: no text-mode open() may rely on the platform default codepage.
151+
# cp1252 on the Windows dev machine, UTF-8 on this runner -- the same file
152+
# read on both yields different bytes, so this is a correctness gate and
153+
# not a style one. It is an AST pass rather than a grep because the
154+
# baseline it replaced was a grep, and that grep undercounted by seven by
155+
# requiring an explicit mode string: open(path) with no mode is already
156+
# text mode. Undecidable call sites fail rather than pass quietly.
157+
- name: Text-mode open() encoding (gate)
158+
run: python scripts/verify_open_encoding.py
159+
160+
# Home-directory literals in CODE (FR-15 / SRS FR-28). Landed with V2-018
161+
# and left unwired until V2-025's audit found it -- the same defect class
162+
# this repository had already found once, when three policy gates existed
163+
# and nothing executed any of them. Classification is by enclosing node,
164+
# so a path inside a docstring is documentation and does not fail.
165+
- name: Home-directory path literals (gate)
166+
run: python scripts/verify_home_paths.py
167+
168+
# Fixed wall-clock timeouts on the long-running pipeline path (NFR-2 /
169+
# SRS NFR-8 / ADR-016). A timeout there is a temporal proxy for "is this
170+
# making progress", and it answers that question wrongly in both
171+
# directions. Detection is an AST pass, not a grep, because "timeout=" is
172+
# ordinary prose in docstrings and comments; classification records
173+
# whether the value is unbounded, configurable, or a fixed default, and
174+
# the one permitted socket-level exception is an enumerated entry whose
175+
# COUNT the gate itself enforces.
176+
- name: Fixed pipeline timeouts (gate)
177+
run: python scripts/verify_no_fixed_timeouts.py
178+
128179
- name: Install production dependencies
129180
run: uv pip install -r requirements.txt --system
130181

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Plugin conformance gate - PRD FR-14 / SRS FR-26 (issue V2-015)
2+
#
3+
# This is a SEPARATE workflow from ci.yml on purpose. ci.yml carries a
4+
# paths-ignore list containing "**/*.md", and the plugin tree is almost entirely
5+
# markdown - a change that added plugin/hooks/README.md would create a hooks/
6+
# directory in the plugin tree and ci.yml would never run. A conformance gate
7+
# that a markdown-only commit can skip is not a gate. This workflow declares no
8+
# path filters at all, so it runs on every push and pull request to main.
9+
#
10+
# The negative-control step below is not decoration. A gate that has never been
11+
# observed to reject anything is indistinguishable from a gate that cannot
12+
# reject anything, so the workflow plants a violation in a scratch copy of the
13+
# plugin tree on every run and fails the build if the gate accepts it.
14+
15+
name: Plugin conformance
16+
17+
on:
18+
push:
19+
branches: [main]
20+
pull_request:
21+
branches: [main]
22+
workflow_dispatch:
23+
24+
concurrency:
25+
group: plugin-conformance-${{ github.ref }}
26+
cancel-in-progress: true
27+
28+
permissions:
29+
contents: read
30+
31+
jobs:
32+
plugin-conformance:
33+
name: Plugin conformance (ADR-010 / ADR-019 CRITICAL)
34+
runs-on: ubuntu-latest
35+
36+
steps:
37+
- name: Checkout repository
38+
uses: actions/checkout@v4
39+
40+
- name: Set up Python 3.11
41+
uses: actions/setup-python@v5
42+
with:
43+
python-version: "3.11"
44+
45+
- name: Install jsonschema
46+
run: pip install "jsonschema>=4.0.0,<5.0.0"
47+
48+
- name: Plugin conformance gate
49+
run: python scripts/verify_plugin_conformance.py
50+
51+
- name: Plugin conformance gate negative control
52+
shell: bash
53+
run: |
54+
set -eu
55+
scratch="$(mktemp -d)"
56+
cp -r plugin/. "$scratch/"
57+
mkdir -p "$scratch/hooks"
58+
printf '{"hooks":{}}' > "$scratch/hooks/hooks.json"
59+
if python scripts/verify_plugin_conformance.py --plugin-root "$scratch"; then
60+
echo "NEGATIVE CONTROL FAILED: the gate accepted a plugin tree carrying a planted hooks artefact"
61+
exit 1
62+
fi
63+
echo "NEGATIVE CONTROL OK: the gate rejected the planted hooks artefact"
64+
65+
- name: Plugin conformance specificity control
66+
shell: bash
67+
run: |
68+
set -eu
69+
scratch="$(mktemp -d)"
70+
cp -r plugin/. "$scratch/"
71+
mkdir -p "$scratch/reference/hooks-notes"
72+
printf 'notes about hooks\n' > "$scratch/reference/hooks-notes/webhooks.md"
73+
printf 'notes about mcp\n' > "$scratch/reference/mcp-notes.md"
74+
printf '{}' > "$scratch/reference/mcp.json.sample"
75+
if python scripts/verify_plugin_conformance.py --plugin-root "$scratch"; then
76+
echo "SPECIFICITY CONTROL OK: the gate accepted unrelated content"
77+
else
78+
echo "SPECIFICITY CONTROL FAILED: the gate rejected content that violates nothing"
79+
exit 1
80+
fi

‎.github/workflows/publish.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ jobs:
3232
with:
3333
python-version: "3.11"
3434

35+
# ADR-007 / SRS FR-29. A dev-mode build resolves the library from a live
36+
# workspace checkout instead of the pinned snapshot, so an artefact
37+
# published from such an environment carries a machine-specific path that
38+
# exists on no user's machine. Runs before any build step, so a dev-mode
39+
# environment cannot reach the upload.
40+
- name: Refuse to publish from a dev-mode environment
41+
run: python scripts/build_library_snapshot.py --check-dev-mode
42+
3543
- name: Install build tools
3644
run: pip install build twine
3745

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Push-gate reachability gate - PRD FR-23 / ADR-017 / HLD v2 section 7.7 (issue V2-025)
2+
#
3+
# This is a SEPARATE workflow from ci.yml on purpose, for the same reason
4+
# plugin-conformance.yml is. ci.yml carries a paths-ignore list containing
5+
# "**/*.md" and "docs/**". This gate protects an ORDERING constraint
6+
# (sequencing_risks.md R-1), and the commit that violates an ordering constraint
7+
# is not confined to any file type - a documentation-only commit that retires the
8+
# equivalence record would be skipped entirely by ci.yml. A gate whose trigger
9+
# excludes a class of commits is a bet that you already know which files matter;
10+
# for a sequencing gate you do not. This workflow declares no path filters.
11+
#
12+
# It is also kept off ci.yml's test matrix so a CRITICAL governance failure
13+
# reports as its own red check rather than as one line inside a test job.
14+
#
15+
# The two control steps are not decoration. They run the SAME script against
16+
# hand-built settings and catalogue files that differ in exactly one axis each,
17+
# so every run re-proves both directions of the biconditional in HLD 7.7:
18+
#
19+
# registration ABSENT + gate UNREACHABLE -> must FAIL (negative control)
20+
# registration PRESENT + gate UNREACHABLE -> must PASS (specificity control)
21+
#
22+
# The specificity control is the one that matters most. An assertion that failed
23+
# whenever the PreToolUse registration was absent would satisfy a careless
24+
# reading of the acceptance criterion and would block the very deletion this gate
25+
# exists to make safe.
26+
27+
name: Push-gate reachability
28+
29+
on:
30+
push:
31+
branches: [main]
32+
pull_request:
33+
branches: [main]
34+
workflow_dispatch:
35+
36+
concurrency:
37+
group: push-gate-reachable-${{ github.ref }}
38+
cancel-in-progress: true
39+
40+
permissions:
41+
contents: read
42+
43+
jobs:
44+
push-gate-reachable:
45+
name: Push-gate reachability (ADR-017 CRITICAL)
46+
runs-on: ubuntu-latest
47+
48+
steps:
49+
- name: Checkout repository
50+
uses: actions/checkout@v4
51+
52+
- name: Set up Python 3.11
53+
uses: actions/setup-python@v5
54+
with:
55+
python-version: "3.11"
56+
57+
- name: Push-gate reachability gate
58+
run: python scripts/verify_push_gate_reachable.py
59+
60+
- name: Push-gate reachability negative control
61+
shell: bash
62+
run: |
63+
set -eu
64+
scratch="$(mktemp -d)"
65+
printf '{"hooks":{}}' > "$scratch/settings.json"
66+
printf '{"schema_version":1,"servers":[{"id":"push-gate","capability":"version-push-gate","repo":"nowhere","entry":"does/not/exist.py","interpreter":"python"}]}' > "$scratch/registry.json"
67+
if python scripts/verify_push_gate_reachable.py --skip-evidence --settings "$scratch/settings.json" --catalogue "$scratch/registry.json"; then
68+
echo "NEGATIVE CONTROL FAILED: the gate passed with no registration and no reachable tool"
69+
exit 1
70+
fi
71+
echo "NEGATIVE CONTROL OK: the gate rejected the state PRD FR-4 must never produce"
72+
73+
- name: Push-gate reachability specificity control
74+
shell: bash
75+
run: |
76+
set -eu
77+
scratch="$(mktemp -d)"
78+
printf '{"hooks":{"PreToolUse":[{"matcher":"","hooks":[]}]}}' > "$scratch/settings.json"
79+
printf '{"schema_version":1,"servers":[{"id":"push-gate","capability":"version-push-gate","repo":"nowhere","entry":"does/not/exist.py","interpreter":"python"}]}' > "$scratch/registry.json"
80+
if python scripts/verify_push_gate_reachable.py --skip-evidence --settings "$scratch/settings.json" --catalogue "$scratch/registry.json"; then
81+
echo "SPECIFICITY CONTROL OK: an unreachable tool alone does not fail the build while the registration stands"
82+
else
83+
echo "SPECIFICITY CONTROL FAILED: the gate fired on a state that violates nothing"
84+
exit 1
85+
fi

‎.gitignore‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,29 @@ htmlcov/
8282
# Windows null device (created accidentally by some shells)
8383
nul
8484
context.toon.json
85+
86+
# Rule 45 SS 2 -- auto-generated diagrams, never committed
87+
/uml/
88+
/drawio/
89+
90+
# Regenerable call-graph analysis output (each >500 KB, blocked by the
91+
# check-added-large-files pre-commit gate). Rebuild from source per
92+
# docs/phase-5-uml/callgraph_coverage_probe.md.
93+
docs/phase-0-reverse-engineering/ast_call_graph.json
94+
docs/phase-0-reverse-engineering/codebase_kg/edges.json
95+
docs/phase-0-reverse-engineering/codebase_kg/nodes.json
96+
97+
# ADR-007 pinned library snapshot -- BUILT, never committed. Measured against
98+
# library 29.73.0 on 2026-08-03: 9,009,526 bytes across 513 files, of which
99+
# 8,045,233 bytes are the 508 agent personas. Two independent reasons not to
100+
# commit it, either sufficient on its own:
101+
# 1. skills_all.json projects to 537,671 bytes, over the 512,000-byte
102+
# check-added-large-files gate above. Committing is blocked, not merely
103+
# undesirable.
104+
# 2. FR-16 is "bundle, do not duplicate". Committing 9 MB of library content
105+
# into this repository's history is the duplication FR-16 argues against.
106+
# It still ships to users: ADR-007 calls it a BUILD-time snapshot and ADR-008
107+
# distributes through a separate marketplace repo, so the publish step builds it
108+
# into the published artefact via
109+
# "python scripts/build_library_snapshot.py --release --verify".
110+
/plugin/snapshot/

0 commit comments

Comments
 (0)