3 Commits

Author SHA1 Message Date
Edward Cheng-I Wu adc38300da feat: register write-scope guard launcher degradations in degradation_registry.json (#769) (#792)
* feat: register write-scope guard launcher degradations in the degradation registry (#769)

The registry claims to index every graceful-degradation mechanism in the
suite, but hooks/run_guard.sh's documented degraded states had no rows,
and the #757 prose table in docs/CONTROL_AVAILABILITY.md stood up a
second, unpinned authority for those facts.

Four write_scope_guard_* rows added (no-python, no-git-bash,
no-timeout-binary, subprocess-misbehaves), each with verbatim D3
authority anchors into hooks/run_guard.sh + the README Requirements
bullet; pinned_by names scripts/test_run_guard_launcher.py where a
CI-executable pin exists (the Windows-without-Git-Bash path never
executes the launcher, so its row honestly carries no pin). Registry
1.2.0 -> 1.3.0; _EXPECTED_MECHANISMS updated in the same commit (D5
lock semantics). The CONTROL_AVAILABILITY degradations table now
declares itself a convenience summary backpointing at the registry.

Closes #769

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZwcSFBwiJBZEtsSTcCxq

* review: address codex findings on #769 — permission phrasing, no-timeout decision forwarding, launcher-internal failure coverage

- Rows no longer claim "writes are never blocked" or relitigate what an
  'allow' decision would do: the launcher emits no permissionDecision,
  so the session's normal permission rules still decide.
- The no-timeout row's terminal_policy_effect states that the healthy
  watchdog fallback forwards the guard's real decision (including deny);
  only an overrun resolves to pass-through.
- The misbehaves row now also covers the two remaining documented
  launcher-internal degradations (SELF_DIR self-resolution failure and
  the POSIX payload-length cap on multi-megabyte Writes), with verbatim
  anchors; CHANGELOG + CONTROL_AVAILABILITY backpointer updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZwcSFBwiJBZEtsSTcCxq

* review: round-2 codex findings on #769 — per-row quantifiers, actual validity-check shape, payload-edge honesty

- terminal_policy_effect now speaks per failure path, not "every degraded
  path"; the no-Git-Bash row states the hook simply does not run.
- The misbehaves row names the launcher's ACTUAL validity check (a JSON
  object carrying a top-level hookSpecificOutput key — deliberately
  shallow, not full hook-schema validation).
- The multi-megabyte payload edge is recorded as a documented accepted,
  untested case with no pinned outcome — no deterministic claim.
- The no_python row's authority anchor swaps to the no-permissionDecision
  pass-through line (the launcher's disputed 'allow' comment is
  pre-existing text this PR neither adds nor endorses).
- CONTROL_AVAILABILITY prose quantifier fixed to match ("none of which
  ever blocks", with the no-timeout forwarding stated).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZwcSFBwiJBZEtsSTcCxq

* review: round-3 codex findings on #769 — payload edge split into its own no-pinned-outcome row

- write_scope_guard_payload_capacity_edge becomes a dedicated row whose
  every field honestly declares "no pinned outcome" — the misbehaves
  row's pass-through claims are now unconditionally true for its own
  failure classes (registry 20 -> 21 rows, D5 lock updated).
- CONTROL_AVAILABILITY prose reworded: degraded states never INTRODUCE a
  block; the no-timeout swap keeps the guard operating normally (real
  decisions, including deny, still apply).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NZwcSFBwiJBZEtsSTcCxq

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 12:54:36 +08:00
Edward Cheng-I Wu 17bf063456 docs: CI workflow enforcement-class table + inventory lint (#755) (#774)
* docs: CI workflow enforcement-class table + WC-1/WC-2 lint (#755)

docs/ARCHITECTURE.md gains §7.1: all 14 workflows classified by
trigger / what it checks / enforcement class (blocking / advisory /
administrative / post-push detection) / bypass token, with the honest
count line (8 blocking on at least one event class, 2 advisory, 1
administrative, 3 post-push detection) and the explicit statement that
tag workflows detect after the push — their stop-power is the
maintainer acting on the failure. Per-workflow facts verified against
the workflow files (eval-harness ack token + PR-only gating; changelog
gate release/** head scope; pytest path filters; the three tag
triggers).

CONTRIBUTING release-checklist prose now points at the classification
instead of implying uniform CI enforcement.

Lint (same-PR drift-point discipline): check_workflow_classification.py
— WC-1 inventory sync both directions (a new, renamed, or removed
workflow fails CI until the table matches; duplicates refused), WC-2
class cells begin with the closed four-term vocabulary. Class
semantics stay review-owned (degradation-registry posture). 9 mutation
tests; wired into spec-consistency.yml + the pytest manifest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* refactor: apply /simplify + codex R1 — table accuracy + lint hardening (#755)

Review round (3 cleanup agents + codex gpt-5.6-sol xhigh R1), findings
deduped and applied:

Table accuracy (codex 3 P2 + 1 P3, cleanup F2/F3): every trigger cell
now states its actual branch/path/tag filters (repository-hygiene and
command-invariants had birth-drifted cells; several rows omitted
targeting-main scopes); freshness-check reclassified honestly
(Advisory for staleness, but malformed protocol metadata is a hard
failure); bypass cells say "justification requested, not
machine-validated" (both workflows accept the bare token);
command-invariants "what it checks" gains its other two enforced
checks; bypass column normalized to "none"; the legend absorbs the
tag-workflows sentence and the duplicated qualifier prose is trimmed.

Lint hardening: section extraction switches to the shared
_skill_lint.heading_section (exact full-line heading incl. the #755
anchor, fence-aware — 15 fewer bespoke lines); rows parse once with
escaped-pipe-aware cell splitting; the inventory glob covers *.yaml;
WC-2 matches vocabulary terms as whole words (Blockingg fails); the
arity guard moves under WC-1 with a test; new WC-3 recomputes the
bolded count line from the Class column (the honesty sentence can no
longer self-invalidate when a workflow is added); new WC-4 pins every
[bypass-token] in a Bypass cell to verbatim presence in its workflow
file. 14 mutation tests.

Surfaces: docs/CONTROL_AVAILABILITY.md corrects its "on every change"
claim and links §7.1; the ARCHITECTURE "How to read" §7 bullet indexes
the CI sub-view.

Skipped with reason: read_or_exit2 exit-2 convention (sibling lints in
this fleet use the exit-1 missing-doc violation shape; consistency
wins).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R2 findings — .yaml fixture parity + comment-blind WC-4 (#755)

- The mutation fixture copies *.yaml alongside *.yml, so a future
  .yaml workflow with a valid row passes the fixture as it passes the
  real lint.
- WC-4 strips full-comment lines before the token search: a renamed
  executable token surviving only in a YAML comment no longer
  satisfies the pin (token in a non-comment echo/log string recorded
  as an accepted edge). Mutation test added (15 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R3 finding — tag pushes reach three more workflows (#755)

GitHub Actions matches tag pushes on unfiltered or paths-only push:
triggers (paths filters are not evaluated for tags), so
spec-consistency, command-invariants, and freshness-check also run on
every v* tag push — where their failures are post-push detection like
the tag-only workflows. Trigger cells amended and a subtlety note
added above the table; "three tag workflows" narrowed to "three
tag-only workflows".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R4 finding — malformed token spellings fail loudly (#755)

Any bracketed span in a Bypass cell must be a well-formed
[lowercase-hyphen] token: a typo like [skip_cooldown] now yields a
WC-4 violation instead of silently falling out of the token grammar.
Mutation test added (16 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R5 finding — whitespace token typos caught (#755)

The any-bracket span matcher now accepts any non-] content, so
[skip cooldown] (space typo) reaches the well-formedness check and
fails loudly. Mutation test added (17 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R6 finding — bogus rows fail instead of dropping out (#755)

Every pipe row in the section that is not the header or the separator
must open with a backticked workflow filename; a malformed row now
yields a WC-1 violation instead of silently leaving the inventory and
the WC-3 count. Mutation test added (18 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* test: mirror docs/ARCHITECTURE.md into the CA fixture (#755)

The new CONTROL_AVAILABILITY link to ARCHITECTURE §7.1 made the #768
test fixture (which mirrors only the files the doc links) miss its
target, failing CA-1 in the fixture tree while the real tree passes —
caught by CI, not locally, because the local sweep re-ran the lint but
not its sibling test file. ARCHITECTURE.md joins the mirrored list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 09:03:53 +08:00
Edward Cheng-I Wu 43a02bf7e2 docs: per-channel control-availability matrix (#757) (#768)
* docs: per-channel control-availability matrix (#757)

Add docs/CONTROL_AVAILABILITY.md — one row per enforcement mechanism,
one column per install channel (plugin / skills copy / repo clone /
Cowork / claude.ai Project / Claude Science / Pi), with honest
active / conditional / absent cells, per-channel notes citing the
existing scattered sources (README Requirements, SETUP methods,
pi/README.md, hooks/run_guard.sh), and the guard's environment
degradation table. Linked from README (Requirements + SETUP pointer)
and SETUP (Installation methods intro).

Evidence re-verified against the working tree: the channel set has
grown past the six named in the issue (SETUP now also documents
Cowork and the claude.ai 4a/4b split), so the matrix covers all
seven documented channels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* refactor: apply /simplify pass + add CA-1..CA-3 defrift lint (#757)

Simplify round (4-agent review, findings deduped):
- Drop the 'How to read an integrity claim' section (it had already
  drifted from the matrix) and the all-identical Upstream row; both
  replaced by one legend sentence and one paragraph.
- Move channel-scoped caveats (Cowork / claude.ai / Claude Science / Pi)
  from per-cell footnotes into a 'Channel-wide limitation' column of the
  channel table; notes drop from 11 to 7.
- De-drift row labels: no inline allowlist contents (canonical list is
  pinned by check_tools_allowlist.py), no exhaustive feature list, no
  hard-coded Claude Code minimum version (lives in SETUP Method 0).
- README: single slimmed pointer (second link and both enumerations
  removed); pointer mirrored to the four translated READMEs and
  docs/SETUP.zh-TW.md.
- Degradations table scoped to actual guard degradations (the slash-form
  version row was misfiled); registry backpointer added; guard-launcher
  registry registration split to #769.

Lint (per the new-claim-surface-needs-lint-in-same-PR discipline):
- scripts/check_control_availability.py — CA-1 links/anchors resolve,
  CA-2 every SETUP '### Method' heading reachable from the channel
  table, CA-3 README + SETUP inbound links pinned. Cell semantics stay
  owned by code review (degradation-registry posture).
- 9 mutation tests; wired into spec-consistency.yml + pytest manifest
  (150 entries).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R1 findings — 4 P2 accuracy corrections (#757)

- SessionStart announce/update-reminder row: Conditional, not Active
  (bash launcher on Windows needs Git Bash; reminder needs curl) — new
  note 8.
- Cross-model note 6 no longer claims credentials+curl universally; the
  citation-only Codex subscription transport is named as the alternative
  transport behind the same consent boundary.
- Pi channel limitation reworded: the wrapper supplies no orchestration
  but uses an installed Pi capability when available.
- 'Enforcement mechanisms' claim language aligned to 'controls' in the
  purpose statement and all five README pointers (consistent with note
  7's trust-based posture).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R2 findings — lint mis-pass cases + note-8 wording (#757)

- CA-1 link grammar accepts optional quoted titles so a titled dead
  link cannot silently skip the check.
- CA-2 counts only fragments on links whose resolved destination IS
  docs/SETUP.md — a same-slug anchor into a copied file no longer
  satisfies method coverage.
- CA-3 checks resolved link destinations, not a filename substring — a
  label that keeps the filename while the target moves now fails.
- Note 8: singular SessionStart hook (hooks.json defines one; the
  announce script runs the update check internally).
- 3 new mutation tests pinning each mis-pass case (12 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R3 finding — commented-out markdown counts for nothing (#757)

Strip HTML comments before extracting links and headings in all three
invariants: a commented-out inbound link no longer satisfies CA-3, a
commented-out SETUP method heading no longer demands CA-2 coverage, and
a commented-out dead link no longer fires CA-1. Two mutation tests pin
both directions (14 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R4 finding — GFM type-2 HTML-block semantics (#757)

A line beginning with <!-- opens a raw-HTML block through the --> line
(including trailing text on the closing line) or to EOF if unclosed;
nothing on those lines renders. The comment stripper now models that
line-level behavior before the inline-span strip, so a link after -->
on a comment line cannot satisfy CA-3 and a dead link after an unclosed
comment cannot fire CA-1. Two mutation tests pin both (16 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* test: fix R4 mutation scenario — line-start vs inline comment (#757)

The previous commit's CA-3 HTML-block test inserted the comment mid-line
(inside the blockquote), where GFM renders the link normally and the
lint correctly stays quiet — the test scenario was wrong, not the lint.
Replaced with a whole-line mutation that actually begins with <!--, and
added the inline-comment symmetry case (link still renders → CA-3
satisfied). 17 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R5 finding — block-quoted HTML-block lines (#757)

The type-2 HTML-block rule applies to block-quote content: the stripper
now looks through leading '> ' markers before the line-start test, so
'> <!-- note --> [link]' cannot satisfy CA-3. Deeper CommonMark
laminations are declared out of scope in the docstring (the surfaces do
not use them; a full parser is out of proportion for a maintainer-slip
guard). 18 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R6 finding — repo-containment on CA-1 targets (#757)

A relative link that resolves outside the repository root now fails
CA-1 even when the host path exists — an over-deep ../.. slip must not
be masked by an existing host file. Mutation test added (19 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R7 finding — fenced code excluded from extraction (#757)

Fenced code regions render literally, and README/SETUP use fences
today, so they are in-scope: a link inside a fence no longer satisfies
CA-3, and a sample "### Method" heading inside a SETUP fence no longer
demands CA-2 coverage. Fence stripping runs before the comment pass so
a comment opener inside a fence stays literal. Two mutation tests (21
total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

* fix: close codex R8 finding — CommonMark fence-length closing rule (#757)

The fence stripper now tracks the opening run character and length: a
closer must be a same-character run at least that long with only
trailing whitespace, so a four-backtick fence demonstrating an inner
triple-backtick block is no longer closed early. Mutation test added
(22 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EosnA4RdUYgbF2KmZ1DTmc

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 23:57:07 +08:00