@chrono-meta/fh-gate 1.4.78 → 1.4.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/CLAUDE.md +7 -2
- package/knowledge/shared/harness-core/loop_engineering.md +1 -1
- package/knowledge/shared/rules/operational_adaptation.md +144 -1
- package/package.json +12 -2
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/scripts/consent_registry_check.sh +472 -0
- package/scripts/fh_session_load.sh +42 -3
- package/scripts/halffix_propagation_scan.sh +126 -0
- package/scripts/package_coverage_check.sh +18 -1
- package/scripts/pipe_verdict_guard.sh +93 -0
- package/scripts/selfcheck.sh +99 -6
- package/scripts/session_close_check.sh +15 -1
- package/scripts/sidecar_calibrate.sh +85 -0
- package/scripts/test_card_drift_probe.sh +55 -0
- package/scripts/test_consent_registry.sh +434 -0
- package/scripts/test_halffix_lanes.sh +170 -0
- package/scripts/test_ollama_panel_lanes.sh +120 -0
- package/scripts/test_package_coverage_lanes.sh +250 -0
- package/scripts/test_pipe_verdict_guard_lanes.sh +96 -0
- package/scripts/test_sidecar_wait_stdin.sh +13 -1
- package/templates/.git-hooks/pre-commit +73 -5
- package/templates/consent_classes.yaml.example +75 -0
- package/templates/settings.PreToolUse.snippet.json +49 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# consent_classes.yaml — the consent-class registry.
|
|
2
|
+
#
|
|
3
|
+
# Copy to `tracks/_meta/consent_classes.yaml` (gitignored, per-environment) to enable
|
|
4
|
+
# accept-side consent promotion. See `knowledge/shared/rules/operational_adaptation.md`
|
|
5
|
+
# §Consent promotion.
|
|
6
|
+
#
|
|
7
|
+
# WHY A REGISTRY AT ALL (cross-family review, 2026-07-29)
|
|
8
|
+
# The first draft let the running session name the class it was counting approvals against.
|
|
9
|
+
# That defeats the class-inflation guard before it fires: the class does not need to be WIDENED
|
|
10
|
+
# later if it was born wide. Recording three approvals as `sim-conductor` instead of
|
|
11
|
+
# `dispatch read-only sim on a local artifact` silently grants every future sim-conductor mode.
|
|
12
|
+
# So classes are DECLARED here, reviewed by a human, and the session may only count against
|
|
13
|
+
# an entry that already exists. A session may propose a new entry; it may not mint one mid-run.
|
|
14
|
+
#
|
|
15
|
+
# THE FLOOR IS READ FROM THIS FILE, NOT JUDGED AT RUNTIME
|
|
16
|
+
# `sinks` is the field the irreversibility floor is decided from. A session asking itself
|
|
17
|
+
# "is this reversible?" is the wrong arbiter — it is the party that wants to stop being asked.
|
|
18
|
+
# Absent entry / absent file / unparseable entry => NO promotion, keep asking. Unknown is not
|
|
19
|
+
# reversible.
|
|
20
|
+
#
|
|
21
|
+
# TAINT
|
|
22
|
+
# A class inherits the floor if it FEEDS an irreversible sink, not only if it crosses one.
|
|
23
|
+
# `local-commit` below is the worked example: committing is locally reversible, but if a
|
|
24
|
+
# post-commit hook, sync daemon, or CI workflow publishes from that commit, the chain crossed
|
|
25
|
+
# go-public and the class must be marked tainted. Declare `feeds:` honestly — the whole point
|
|
26
|
+
# is that no single step in a bad chain looks dangerous on its own.
|
|
27
|
+
|
|
28
|
+
classes:
|
|
29
|
+
# ---- promotion-eligible example -------------------------------------------------
|
|
30
|
+
- name: dispatch-readonly-sim-local-artifact
|
|
31
|
+
owner: fh-meta:sim-conductor # the gate/skill that does the asking
|
|
32
|
+
mode: blind-target-tier-sim
|
|
33
|
+
target: a single local file under the current repo
|
|
34
|
+
capabilities: [read, dispatch] # read · local-write · network · dispatch · repo-mutation
|
|
35
|
+
sinks: [] # crosses nothing irreversible
|
|
36
|
+
feeds: [] # enables/queues nothing irreversible
|
|
37
|
+
promotion_eligible: true
|
|
38
|
+
lease_days: 30 # standing consent is a renewable lease, never permanent
|
|
39
|
+
|
|
40
|
+
# ---- NOT promotion-eligible: sink is irreversible --------------------------------
|
|
41
|
+
- name: npm-publish
|
|
42
|
+
owner: Pre-Publish Surface Gate
|
|
43
|
+
mode: registry-publish
|
|
44
|
+
target: the package tarball file set
|
|
45
|
+
capabilities: [read, network]
|
|
46
|
+
sinks: [go-public] # irreversible => never promotes, at any count
|
|
47
|
+
feeds: []
|
|
48
|
+
promotion_eligible: false
|
|
49
|
+
reason: "publish is effectively irreversible — a package is live the instant it ships and may
|
|
50
|
+
be cached or forked before any scrub (CLAUDE.md §Pre-Publish Surface Gate)."
|
|
51
|
+
|
|
52
|
+
# ---- NOT promotion-eligible: TAINTED (this is the one that looks safe) -----------
|
|
53
|
+
- name: local-commit
|
|
54
|
+
owner: FH 4-axis commit gate
|
|
55
|
+
mode: git-commit
|
|
56
|
+
target: the working tree
|
|
57
|
+
capabilities: [local-write, repo-mutation]
|
|
58
|
+
sinks: [] # committing itself is reversible
|
|
59
|
+
feeds: [go-public] # ...but CI/sync publishes from it
|
|
60
|
+
promotion_eligible: false
|
|
61
|
+
reason: "taint — reversible in isolation, feeds an irreversible sink. Every step in the
|
|
62
|
+
workflow-edit -> release-notes -> commit -> CI-publish chain is individually
|
|
63
|
+
reversible; the chain is not."
|
|
64
|
+
|
|
65
|
+
# ---- NOT promotion-eligible: unclassified ---------------------------------------
|
|
66
|
+
- name: run-arbitrary-shell
|
|
67
|
+
owner: (none — catch-all)
|
|
68
|
+
mode: unrestricted
|
|
69
|
+
target: unbounded
|
|
70
|
+
capabilities: [read, local-write, network, dispatch, repo-mutation]
|
|
71
|
+
sinks: [unknown] # unknown is NOT reversible
|
|
72
|
+
feeds: [unknown]
|
|
73
|
+
promotion_eligible: false
|
|
74
|
+
reason: "sinks cannot be enumerated, so the floor cannot be decided mechanically. A class
|
|
75
|
+
whose blast radius is unbounded is never a candidate, regardless of approval count."
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_README": [
|
|
3
|
+
"PreToolUse(Bash) hook snippet — TRACKED, so it arrives with a clone. Every .claude/settings*.json",
|
|
4
|
+
"path in this repo is gitignored (confirm with `git check-ignore -v .claude/settings.json`), so a",
|
|
5
|
+
"hook registration cannot itself be tracked. This is the tracked SOURCE that install-wizard merges",
|
|
6
|
+
"into the user's local settings. Shipping the file is not the same proposition as wiring it — that",
|
|
7
|
+
"gap is exactly what #204 found (the SessionStart hook existed from 07-05 and the wizard had never",
|
|
8
|
+
"installed it, for every new user).",
|
|
9
|
+
"",
|
|
10
|
+
"WHAT IT GUARDS: reading a verdict from the wrong end of a pipe. `cmd | tail -5; echo $?` reports",
|
|
11
|
+
"TAIL's status, so a FAILED gate reads as 0 — a degrade toward PASS, the direction that never",
|
|
12
|
+
"announces itself. Measured 6x in this project 2026-07-29..07-31.",
|
|
13
|
+
"",
|
|
14
|
+
"WHY A HOOK AND NOT A FILE LINTER: the plan of record was an S6 class in degrade_direction_scan.sh.",
|
|
15
|
+
"Hand-verifying every `pipe + $?` in this repo's scripts returned 7 hits, 7 of them CORRECT. True",
|
|
16
|
+
"positives in shipped files: 0. All 6 recurrences were in interactively-composed commands, which no",
|
|
17
|
+
"repo scanner reads. The guard belongs where the defect occurs — the Bash call itself.",
|
|
18
|
+
"",
|
|
19
|
+
"ADVISORY BY DESIGN: it warns and exits 0. It never blocks a Bash call, because a mis-read verdict",
|
|
20
|
+
"is re-runnable, and a false block on a developer's own shell trains the --no-verify reflex on the",
|
|
21
|
+
"hooks that DO guard irreversible surfaces (pre-push destructive-op, pre-commit confidentiality).",
|
|
22
|
+
"Escalate deliberately with FH_PIPE_VERDICT_BLOCK=1 if a project wants it to bite.",
|
|
23
|
+
"",
|
|
24
|
+
"Carries no private path, so it belongs in .claude/settings.json (project-local), not settings.local.json.",
|
|
25
|
+
"Merge, do not overwrite: preserve any PreToolUse entries the user already has; replace only the FH",
|
|
26
|
+
"one, keyed by script name.",
|
|
27
|
+
"",
|
|
28
|
+
"Verify after wiring (known pair, both directions):",
|
|
29
|
+
" bash scripts/test_pipe_verdict_guard_lanes.sh # 15 pairs, expect all hold",
|
|
30
|
+
" printf '%s' '{\"tool_name\":\"Bash\",\"tool_input\":{\"command\":\"g.sh | tail; rc=$?\"}}' \\",
|
|
31
|
+
" | bash scripts/pipe_verdict_guard.sh # expect a PIPE-VERDICT R2 warning"
|
|
32
|
+
],
|
|
33
|
+
"project_settings_json": {
|
|
34
|
+
"hooks": {
|
|
35
|
+
"PreToolUse": [
|
|
36
|
+
{
|
|
37
|
+
"matcher": "Bash",
|
|
38
|
+
"hooks": [
|
|
39
|
+
{
|
|
40
|
+
"type": "command",
|
|
41
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/scripts/pipe_verdict_guard.sh\"",
|
|
42
|
+
"timeout": 5
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|