Repository navigation
[docs] - Resync the knowledge and session skills and make the privacy verify script check something - #1645
Conversation
… verify script check something fable-protocol's knowledge base restated CLAUDE.md's invariants, numbers, traps and git workflow and had drifted on four of them (max_context_tokens 8000 vs 16000, chunk 512/50 vs 256/32, a num_ctx floor of 13,596 vs the current 21,596, and a `harness` package in the I6 list that #1367 removed); those blocks now point at CLAUDE.md §2-§5/§10 and keep only what must be known cold. Its version history is condensed to a dated list, the audit log is described as keyed HMAC-SHA256 fingerprinting (not plain SHA-256), and the "/index/build is not key-gated" decision row says "while the key is unset". cyclaw-gotchas expires two gotchas under its own rule (the `--ignore-installed PyYAML` trap retired by the hashed locks in #1598, and the #1247 Codex P2 made moot by #1367), counts six stdlib guards, aligns its test guidance with CLAUDE.md §5, and stops asserting bare python3 is 3.11. cyclaw-privacy's heading loses the old cyclaw-advisor name, its audit and memory bullets name query_fingerprint and the HMAC key file, the data map gains logs/cyclaw.log and the eval ledger, and verify.sh now checks the shipped privacy posture in config.yaml (fingerprinted audit, redaction on, no off-box context, memory/auth off, third-party log level above DEBUG) instead of echoing "consistent (from previous analysis)"; bootstrap.sh runs it. CyClaw-Sandbox is reconciled against main @ 663a8bb: five ladders not six, the Guardrails table's historical numbering, the core six in every I6 statement, the console's third JS file and seven panels, /index/build's key gate once CYCLAW_API_KEY is set, the realism-tier auto-detection that only run_full_verification.py performs, the real coverage of test_terminal_consoles.py and gate_runtime_check.py, the auth UI that auth_admin.js already provides, contiguous step numbers, and the driver.sh/locks install in place of the retired requirements.txt recipe. run_full_verification.py's Phase 6 and test-specifications.md R1-R3 grepped gate.py for a redaction tuple that moved to utils/logger.py and utils/public_errors.py, so three checks failed on every run; they now read the real home and call public_error. verify.sh's header claimed nine stages (it has seven), and the /CyClaw-Sandbox wrapper claimed the skill opens a draft PR (CLAUDE.md §9: it opens none). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LbrgG4NUbR3sUTgNMfKXpU
… and duplicated rows fable-protocol's §8.2 and §9 restated §1/§6/§1.5/§2.6 inside the same file and the knowledge base repeated §5.1's CWE-1022 note; cyclaw-gotchas' human-run section and Guardrails restated the Run section and CLAUDE.md; CyClaw-Sandbox's first two Gotchas restated Step 2 and the operator map, Known residual 1 ran to ten lines, and Guardrails rows 1-5 restated CLAUDE.md §3 (collapsed to one row pointing at invariant-guard; the skill-specific rows 7-26 are unchanged). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LbrgG4NUbR3sUTgNMfKXpU
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
…-13) High-signal since 3fe8217 (#1619): #1637 telemetry/egress inventory completion (fail-closed kill block, full spawn-site table for PowerShell/pip/brew/git-remote cat-3, agent-tooling boundary, OLLAMA_HOST pin, secret-env wrappers); #1643/#1645 docs resyncs correcting I6/harness and checker skill drifts. Graph remains 12 nodes.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 33912e822e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ("logging.audit.include_query_hash", get("logging.audit.include_query_hash", True) is True, | ||
| "audit stream stores keyed HMAC fingerprints, never raw query text"), |
There was a problem hiding this comment.
Read include_query_hash from audit_fields
When the shipped privacy toggle is changed to logging.audit_fields.include_query_hash: false, this lookup still returns its True default because it queries the nonexistent logging.audit.include_query_hash path. The verifier therefore reports the privacy posture as healthy even though audit_log starts persisting redacted raw queries; use the actual audit_fields path or the production include_query_hash helper. The same incorrect key is also published in the updated privacy skill.
AGENTS.md reference: AGENTS.md:L5-L7
Useful? React with 👍 / 👎.
| except ImportError: | ||
| print("SKIP: PyYAML not importable (fresh container); nothing checked") | ||
| sys.exit(0) |
There was a problem hiding this comment.
Do not pass when PyYAML is unavailable
In the repository's verify-skills workflow, cyclaw-privacy is assigned the stdlib profile, which installs no PyYAML, so this branch is taken on every CI run and exits successfully after checking nothing. That makes the newly added posture verifier appear green in its primary automated environment; either classify the skill under the YAML profile or return a non-success skip status rather than treating missing prerequisites as verification.
AGENTS.md reference: AGENTS.md:L44-L49
Useful? React with 👍 / 👎.
| policy: | ||
| privacy: | ||
| redact_secrets_like: |
There was a problem hiding this comment.
Keep privacy under the existing policy mapping
This example now declares the top-level policy key twice. PyYAML silently keeps only the second mapping, so parsing or copying the documented hybrid configuration drops the entire fallback block immediately above it; strict YAML parsers may reject it outright. Nest privacy alongside fallback under the first policy mapping so the test specification represents the actual config.yaml structure.
AGENTS.md reference: AGENTS.md:L5-L7
Useful? React with 👍 / 👎.
| has_env_scan = "os.environ.items()" in logger_src and "def redact_sensitive" in logger_src | ||
| log(f" [{'PASS' if has_env_scan else 'FAIL'}] redact_sensitive scans every os.environ value (Grok + Anthropic parity)") | ||
| phase.checks.append(Check("env_value_redaction", has_env_scan)) |
There was a problem hiding this comment.
Test the secret-name filter instead of any env iteration
This check and its PASS message claim that every environment value is redacted, but redact_sensitive only replaces values whose names match SECRET_POLICY and whose values are at least eight characters. Merely finding os.environ.items() therefore passes even if provider names stop being classified, and arbitrary environment values demonstrably remain unchanged; exercise the classification/redaction behavior directly or narrow the advertised guarantee.
AGENTS.md reference: AGENTS.md:L33-L33
Useful? React with 👍 / 👎.
| ("logging.third_party_level", str(get("logging.third_party_level", "INFO")).upper() != "DEBUG", | ||
| "third-party log level above DEBUG (httpcore DEBUG writes Authorization headers)"), |
There was a problem hiding this comment.
Reject NOTSET as an unsafe third-party log level
When logging.third_party_level is set to NOTSET, this predicate passes because the value is not the literal string DEBUG, but production resolves it to logging level 0 and admits DEBUG records, including the httpcore Authorization-header output this check is intended to prevent. Validate that the resolved logging level is at least INFO, rather than checking inequality with one spelling.
AGENTS.md reference: AGENTS.md:L33-L33
Useful? React with 👍 / 👎.
| - ~~**`--ignore-installed PyYAML` reinstalls everything, not just PyYAML**~~ | ||
| — expired 2026-10-13: every documented install now uses the hashed | ||
| `locks/` with `--require-hashes` (#1598), no install line carries | ||
| `--ignore-installed`, and `tests/test_macos_scripts.py` asserts the old |
There was a problem hiding this comment.
Keep the PyYAML reinstall warning active
The claim that no documented install still uses --ignore-installed is contradicted by the active docs/! How-To-Guides/OLLAMA_SETUP.md command, which still runs pip install -r requirements.txt -c constraints.txt --ignore-installed PyYAML. Marking this gotcha expired hides the resolver/torch-floating risk from users following that guide; update the remaining guide to the hashed-lock flow before retiring the warning.
AGENTS.md reference: AGENTS.md:L35-L37
Useful? React with 👍 / 👎.
| **The six invariants and the load-bearing numbers** live in `CLAUDE.md` §3 | ||
| and §2 ("Load-bearing numbers"), which `doc-sync` D3/D8 keep honest; this | ||
| file stopped carrying its own copy on 2026-10-13 after the copy drifted | ||
| (`max_context_tokens`, chunk sizes and the `num_ctx` floor were all a |
There was a problem hiding this comment.
Do not claim D3 and D8 cover all invariants and numbers
doc-sync D3 checks only five numeric keys (api.port, retrieval.min_score, retrieval.rrf_k, api.graph_timeout_sec, and personality.soul_max_chars), while D8 checks only the graph node count; neither validates the six security invariants, max_context_tokens, chunk sizes, or the num_ctx floor cited here. This statement gives agents false confidence that the authoritative copy cannot drift, so name the narrower coverage and point invariant/context claims to their actual checkers.
AGENTS.md reference: AGENTS.md:L5-L7
Useful? React with 👍 / 👎.
| | Auth routes | `gate_auth.py` | 13 paths (`/auth/setup-status`, `/bootstrap-password`, `/login`, `/logout`, `/whoami`, `/users` GET+POST, `/password`, `/users/{u}/password`, `/users/{u}/role`, `/users/{u}/disable`, `/users/{u}/enable`, `/users/{u}` DELETE, `/audit/summary`); every route exists and answers **503** (not 404) when `auth.enabled` is false (the shipped default); identity attaches to `POST /query` only when an `AuthManager` exists; `audit` role is forbidden from `/query`; account lockout answers **423**; a raced duplicate username or device-token label is a typed conflict (`AuthUserExists`/`AuthTokenLabelExists` -> **409**) | `test_auth_admin_contract.py`, `tests/test_due_diligence_invariants.py` | | ||
| | Memory routes | `gate_memory.py` | `/memory/status` is always 200 (probeable when the subsystem is off); `/memory/{facts,episodes,proposals,propose,apply,reject}` and `/query/export/html` are **404** when their toggle is off (all ship false); flag resolution goes through `memory/flags.py` -- the fact-retrieval switch is `memory.facts.retrieval_enabled` (legacy `memory.facts.enabled` honored with a one-time warning), the status payload reports `facts_retrieval_enabled`, and every flag echo is a strict boolean (`is True` -- a YAML `"false"` string reports off) | `test_memory_isolation.py`-style isolation + live probe | | ||
| | Terminal console | `static/terminal.html` + `static/terminal.js` (CSP forces `script-src 'self'`, so the console's JS logic lives in the sibling file -- read both together) | 5 toolbar panels (Soul/Sync/Agentic/FS/SQL); the confirm dialog's generic `handleConfirm(confirmed, entryId, onlineProvider)`; the four slash commands `/users /admin /audit /help` (everything else is a toolbar button or a RAG query, not a command) | `tests/test_terminal_contract.py` (reads `terminal.html + terminal.js` combined; pins the 5 POST-only paths) | | ||
| | Terminal console | `static/terminal.html` + `static/terminal.js` + `static/auth_admin.js` (CSP forces `script-src 'self'`, so the console's JS logic lives in the sibling files -- read all three together) | 7 toolbar panels (Soul/Sync/Agentic/FS/SQL, plus Users and Audit, which stay hidden until `auth.enabled`); the confirm dialog's generic `handleConfirm(confirmed, entryId, onlineProvider)`; the four slash commands `/users /admin /audit /help` (everything else is a toolbar button or a RAG query, not a command) | `tests/test_terminal_contract.py` (reads the HTML and JS combined; its `_POST_PATHS` pins every POST-only path -- derive the count from the file, it grows) | |
There was a problem hiding this comment.
Include auth_admin.js in the route contract
This row says the terminal contract reads all three browser assets and that _POST_PATHS pins every POST-only path, but tests/test_terminal_contract.py::_console_source concatenates only terminal.html and terminal.js. Consequently the Users panel's dynamic POST /auth/users, role, password-reset, and delete calls in auth_admin.js are not checked against gateway routes or HTTP methods, so those endpoints can drift while the advertised contract remains green; include the third asset and account for its dynamically constructed paths.
Useful? React with 👍 / 👎.
| even with auth off; `/index/build` is loopback+same-origin gated and, once | ||
| `CYCLAW_API_KEY` is set (as it is in this step), also key-gated -- it is | ||
| deliberately NOT key-gated while the key is unset so a first-run build cannot |
There was a problem hiding this comment.
Export the generated session key before live probes
On a fresh shell, Step 2 merely prints a generated key and never assigns it to CYCLAW_API_KEY, yet the live-gate command later expands that variable and this changed text claims it is set. Following the procedure therefore starts the server with an empty key—or aborts under set -u—and the authenticated soul/ops console probes cannot exercise the documented path; assign and export the generated value before launching the server.
AGENTS.md reference: AGENTS.md:L20-L22
Useful? React with 👍 / 👎.
Proposed changes
Skill-catalog resync, part 3 of 3 (#1642 covers the loop/persona skills; #1643, the checker skills, is merged; all three touch disjoint files). This one covers
fable-protocol(and itsreferences/knowledge-base.md),cyclaw-gotchas,cyclaw-privacyandCyClaw-Sandbox, plus their sibling scripts and the two thin command wrappers.max_context_tokens 8000(config: 16000), chunk512/50(256/32), anum_ctxfloor of 13,596 with the env file "at 16384" (now 21,596 and 32768), and aharnesspackage in the I6 list that [infra] - Remove the Python coding-harness console; CG-agent-harness owns that role #1367 removed. Those blocks are replaced by pointers to CLAUDE.md §2–§5/§10 plus the two or three facts worth knowing cold. Also: the audit log is keyed HMAC-SHA256 fingerprinting, not plain SHA-256; "CLAUDE.md §5 asks for conventional commits" is false (the hook enforces[prefix] - sentenceon every commit); "/index/build not API-key gated" now says "while the key is unset"; the chromadb CVE acceptance lives in SECURITY.md;python-coding-agent(no such skill) is gone from the trajectory note; the 65-line consolidation + version history is a dated 15-line list;SKILL.md§5.4 says soul.md is governed (I5), not immutable, matching the knowledge base's own words; the Opus 4.8→5→5.5 bracket is one sentence.--ignore-installed PyYAMLtrap (every install is now the hashedlocks/from [infra] - Park hashed pip locks in locks/ and drop temp paths from comments #1598;tests/test_macos_scripts.pyasserts the old constraints rewrite is gone) and the [fix] - guard POST /api/agent/run with the harness's existing generation gate #1247 Codex P2 made moot by [infra] - Remove the Python coding-harness console; CG-agent-harness owns that role #1367. "Barepython3is 3.11 and always will be" is false on the current image (3.13); the venv step describes the uv path the driver actually takes;checksruns six guards, not five (dotenv-guard was added;driver.sh's header fixed too); the test section follows CLAUDE.md §5 instead of "then the whole suite once"; advisory count 5→4 chromadb;doc_sync.pycoverage is D1–D13.CyClaw-Advisor; the audit bullet namesquery_fingerprint(keyed HMAC, key env/file is itself a data element) and theinclude_query_hash: falseregression; episodes usequery_fingerprint, nothash_query; the data map gainslogs/cyclaw.log(with thethird_party_levelDEBUG key-leak caveat fromconfig.yaml) andlogs/evals/eval_runs.jsonl.verify.shused to print "Dep files consistent (from previous analysis)" without checking anything; it now readsconfig.yamland asserts the posture SKILL.md cites (fingerprinted audit, redaction on,sk-antshape present, no off-box context by default, memory/auth off, third-party log level above DEBUG), exit 0/2, clean SKIP without PyYAML.bootstrap.shprints the SHA to cite and runs it.sequence_detectgrep coversgate*.py); the gate-routes row adds the console-session routes and saysgate_runtime_check.pypins a subset; the console row has 7 panels and 3 JS files;/index/buildis key-gated onceCYCLAW_API_KEYis set (which Step 5 sets); onlyrun_full_verification.pyauto-detects the realism tier;test_terminal_consoles.pyandverify.share described by what they run;smoke.sh's suite is section F; Known residual 4 reflects the Users/Audit UIauth_admin.jsalready provides; steps are numbered 1–9 without a gap; Step 2'srequirements.txt --ignore-installed PyYAMLrecipe is replaced bydriver.sh venv/ §8; the memory-orchestrator history paragraph and the mock-embedding internals are trimmed to pointers.run_full_verification.pyPhase 6 greppedgate.pyforANTHROPIC_API_KEY,sk-ant-andGROK_API_KEY— none has been there since redaction moved toutils/logger.py(redact_sensitive,_MINIMUM_SECRET_PATTERNS) andutils/public_errors.py, so three checks failed on every run; the phase now checks the real home and exercisespublic_error.test-specifications.mdR1–R3 and its YAML snippet had the same stale targets and the wrong config path (logging.audit→policy.privacy);verify.sh's header claimed nine stages (it has seven; the SKILL.md table was already right);.claude/commands/CyClaw-Sandbox.mdclaimed the skill opens a draft PR and tests a "harness console" (CLAUDE.md §9: opens no PR; the harness is gone).invariant-guard— the skill-specific rows 7–26 are unchanged.Net: 364 insertions, 443 deletions across 12 files.
Invariant / Governance Impact: none of the six invariants or I6 changes; nothing outside
.claude/is touched. The pre-commit gate flagged the agent-instruction surface (every change is in the diff) and asked two secure-by-design questions aboutrun_full_verification.py:958-969: those lines readutils/logger.py/utils/public_errors.pysource text and runpublic_erroron a fakesk-ant-api03-…string — no new config input, no new telemetry, and the fake key is asserted absent from the output.Types of changes
Scope note: Docs + audits, plus skill-local verify/bootstrap scripts and the Sandbox swarm's Phase 6.
Benefits / why
/fable-protocol; it was teaching four wrong numbers and a dead package name. Pointing at CLAUDE.md meansdoc-syncD3/D8 now guard those facts instead of a prose copy nobody checked.cyclaw-privacy/verify.shis classifiedstdlibinci.yml's verify-skills matrix; it now actually verifies the posture a privacy review cites, so a config change that turnsinclude_query_hashoff or routes context off-box fails that leg.Risks to monitor
cyclaw-privacy/verify.shreadsconfig.yamlkeys by name; a rename ofpolicy.privacy.*orlogging.audit.include_query_hashwill fail it loudly (exit 2), which is the intent, but the message names the key so the fix is obvious.run_full_verification.py's new Phase 6 could not be executed in the authoring sandbox (no torch/fastapi; the proxy denies the model host); it was syntax-checked and its targets grepped by hand, and CI'sverify-skills (CyClaw-Sandbox)leg is green on 33912e8..claude/skills/CyClaw-Sandbox/verify.shis the blocking lifecycle and does not call Phase 6.fable-protocoldescription still names "Sonnet 5.5 vs Opus 5.5" while the body routes on "Sonnet 5"; the model-naming calibration is the owner's and was left alone.Checklist
docs/THREAT_MODEL.md,INVARIANTS.mdandSECURITY.md(and any relevant Phase docs)cyclaw-privacy/verify.shandbootstrap.sh(both pass, exit 0),cyclaw-gotchas/verify.sh(OK, re-run after the trim),doc_sync.py(0 drift),py_compile+bash -non every edited script, ruff onrun_full_verification.py(the 7 findings predate this branch, same count on main).Further comments
Measured across the three PRs, the 23
SKILL.mdfiles plus the fable knowledge base go from 5,340 to 4,971 lines, a 6.9% trim (the first pass landed at 2.1%; the owner asked for the rest, which came from duplicated CLAUDE.md content, boilerplate repeated across the four loops, and dated history — never from a rule stated only once). Left for the owner: whether §8.2–8.3 offable-protocol(the communication contract and "the pattern") belong only in the user-level copy per PROJECT_RULES.md's PII rule — they carry no DOB/email/location, so they were left as is.Suggested merge order of open PRs
Trial merges verified (
git merge --no-commit --no-ff+ abort) against main @ 659d910 on 2026-10-13 (after #1637, #1638, #1640 and #1643 merged). This PR and #1642 touch disjoint files under.claude/; neither overlaps the other open PRs.#1641 · Safe ✅
Impact: CI bwrap/AppArmor workflow; independent.
Action: merge anytime.
#1642 · Safe ✅
Impact: loop/persona skill files; disjoint from this PR.
Action: merge anytime.
#1644 · Safe ✅
Impact: CI bwrap/AppArmor selection; independent of this PR.
Action: merge anytime.
#1645 (this PR) · Safe ✅
Impact: fable-protocol, cyclaw-gotchas, cyclaw-privacy, CyClaw-Sandbox files only.
Action: merge anytime; re-run the trial merge if main moves.
#1646 · Safe ✅
Impact: otel-hardening inventory; independent of this PR.
Action: merge anytime.
ELI5
Four of the repo's "what to know" sheets for AI sessions had memorised an older version of the project: wrong context-window and chunk numbers, a package that was deleted months ago, an install recipe that no longer exists, and a privacy "verifier" that printed "all good" without looking at anything. One sandbox self-test had also been failing quietly for a while because it searched the wrong file for the secret-redaction code. This PR makes the sheets point at the single source of truth (
CLAUDE.mdandconfig.yaml) instead of copying it, turns the privacy verifier into a real check of nine config settings, points the self-test at where the redaction code lives now, and cuts the paragraphs that said the same thing twice. Nothing in the running server changes. What could still go wrong: if someone renames one of those nine config keys, the privacy verifier goes red with the key name in its message; the sandbox self-test's new checks ran green in CI's advisory verify-skills leg, and the first fullrun_full_verification.pyon a real venv is the remaining proof.🤖 Generated with Claude Code
https://claude.ai/code/session_01LbrgG4NUbR3sUTgNMfKXpU
Generated by Claude Code
Last updated: 2026-10-13 04:46 ET