Repository navigation
Conversation
…rise (#264); BUG-156 + preflight row 28 (#263) #264: `mxcli init` re-seeds .claude/lint-rules/ and the toolkit rules lose their header. lint-gate.sh never read .claude/.mxtk-lint-receipt, so the re-seed showed up as a rise on the next exec (CONV010 26 -> 169 on a pages-only script), or sat for days while the baseline absorbed stock counts. - project-bin/lint-gate.sh: before linting, each rule in the receipt is md5'd; a mismatch with the header gone (or a missing file) is named, the remedy `<toolkit>/bin/sync-project.sh <project>` is printed, and the run exits 1 with `BLIND — lint rules reverted to stock`. lint-last.json gets verdict BLIND-RULES-REVERTED and no total (no stale total for metrics-ledger). --update-baseline is refused over reverted rules. A headered edit is left to the installer's LOCALLY MODIFIED report; no receipt means no check; LINT_RULES_REVERTED_OK=1 lints anyway, with the warning. - project-bin/exec.sh: run_lint_gate maps that output to LINT_RC 3 / "LINT BLIND: rules reverted to stock (<files>) — run sync-project.sh; delta not attributable". - Guard rules: the receipt is the installer's evidence, not lint-gate's own stamp (rule 6); the remedy (sync-project.sh / the installer) is never blocked and clears it — fixture T6 (rule 7). - tests/wave2/test-lint-receipt-revert.sh: golden receipt from the real installer run into a scratch project; 23/23 here, 11 fail with master's lint-gate.sh. #263 (learned-rule half): BUG-156 in bug-logs/mxcli-bugs.md (bin/bug-lookup.sh BUG-156 and "free room" find it) and STOP row 28 in skills/learned-mdl-preflight.md, quoting the issue's exec error verbatim. Upstream draft prepared, not filed. Field run: the receipt/installer half was field-checked by the maintainer on a copy of a v0.25.0 project (#264 comment: installer restores conv010/data_change, md5s then match the toolkit). lint-gate's new check has not yet run on a live project — fixture only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…268, #267) SEC014 (warning, security tier): an external user role, named in lint-config.yaml as `external_roles: "A, B"` (or "none"), that can write all members, a state enumeration, an audit ...At/...By attribute, or the association its own XPath scopes ownership on. A `_rule` finding when the option is unset, given as a YAML list (mxcli passes it as None), names a role that does not exist, or the catalog is not FULL. CONV009: toolkit-owned copy of the stock rule, which hard-codes 15 and ignores the maxObjects option its own generated config advertises. Reads maxObjects (default kept at 15) and countLoopBodies; replaces the stock file through STOCK-HASHES.txt (conv009 at v0.17.0/0.24.0/0.25.0 plus a full v0.26.0 set, captured with `mxcli init` from the official release assets; test-stock-hashes.sh T4 passes live on all four). Harness: get_option, permissions_for, attributes_for, user_roles, role_mappings mocks. 26 cases in tests/lint-rules/test-batch4-rules.sh. Field run (official mxcli 0.26.0, scratch copies of two models): stock CONV009 5 -> 5 with maxObjects: 20; toolkit CONV009 5 / 10 / 6 at 15 / 10 / 20+loops. SEC014 1 finding with the external role configured, 7 with every role configured, matched by a SQL replay of the catalog. Both rules also load on mxcli 0.17.0 and 0.24.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
The always-on baseline tier counted by bin/render-routing.sh --check was at 79,735 of its 80,000-word cap. This frees 3,772 words (now 75,964) by moving narrative and setup-only text out of baseline files, keeping every rule, STOP, denominator and gate cell in place, each rule still paired with a one-line failure example plus a pointer to the full text. - conversion-runbook.md, source-triage.md, interview-protocol.md: long incident narratives -> one line + pointer; originals verbatim in new on-demand process/baseline-incident-history.md (R1-R20, AR1, ST1-ST4, IP1-IP3). Runbook's duplicate open-questions status table now defers to interview-protocol.md section 4. - agent-roles.md: the six template shapes (setup-only) moved to agents/README.md, which installers never copy (explicit MXTK_AGENTS). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…low", ST1 id Independent review of #315: obligations.tsv still pointed at agent-roles.md for step 8a, which moved to agents/README.md; agent-roles.md said the example bodies are "below"; the history header cited a non-existent section id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…rate opt-out; prune stale receipt rows (#264 review) Review of #308 (MERGE-AFTER-SMALL-FIX): 1. bin/lib/install-lint-rules.sh: a rule whose receipt row exists and whose `# mxtk-lint-rule:` header is gone is restored even when its hash is not in STOCK-HASHES.txt (an mxcli newer than the list, then init). It used to fall into the foreign branch -> LOCALLY MODIFIED, never written, so lint-gate stayed BLIND forever. Same test lint-gate uses; the found file is kept as <rule>.reseeded-<ts>. lint-gate's message adds the fallback `sync-project.sh <root> --upgrade-lint-rules all`. 2. Deliberate opt-out: .claude/lint-rules-disabled.txt. The installer never installs/restores a listed rule and prunes its receipt row; lint-gate skips it. lint-rules/README.md (txt002 row, CONV010 prefix note) now says list-then-delete, with an "Opting out of a rule" section. 3. The installer prunes receipt rows for rules no longer in MXTK_LINT_RULES / MXTK_LINT_RULES_CONFIGURABLE, so a retired rule is not "gone" forever. 4. skills/learned-mdl-preflight.md row 28 cut to trigger + rule + one-line failure (60 words, was ~230); the narrative stays in BUG-156. Fixture tests/wave2/test-lint-receipt-revert.sh T9-T11 (INSTALLER= swaps the installer): T9 uses a genuine stock body (toolkit entity_business_key minus header, md5 d66528c9 = the v0.24.0/v0.25.0 stock hash in STOCK-HASHES.txt) under a scratch toolkit that does not list the hash and whose rule has moved off stock. 38/38 here; 9 fail with master's installer, 21 with master's installer + lint-gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…-SEEN, not a rise (#305 review) A rule sync-project.sh installs after the baseline has no baseline count, so its first hit compared against 0: every exec logged LINT ROSE and exited 1 until someone re-baselined, and the Lint Batch 3 rules fire on ordinary text (TBD, XXX, v1.2, "12 Oct 2026"). - project-bin/lint-gate.sh: a rule absent from the baseline counts is FIRST-SEEN when it is a .star rule in .claude/lint-rules/ (RULE_ID = "...") the baseline did not know: not in the new `star_rules` list --update-baseline now writes, or, for an older baseline, its .star file is newer than the baseline. Printed as `FIRST-SEEN <rule> N finding(s) — new rule, first seen: review and re-baseline with --reason`; exit 0 when nothing else rose; verdict PASS-FIRST-SEEN. Re-baselining it needs --reason (it raises a count), after which it is an ordinary ratcheted rule. Built-in mxcli rules and .star rules known at baseline with zero findings still count as a rise (a baseline omits zero-count rules). - project-bin/exec.sh: the rc-0 row carries `FIRST-SEEN: <rule> (N) — review, re-baseline with --reason`. - tests/wave2/test-lint-gate-first-seen.sh: real toolkit .star files, LINT_JSON input; 17/17 here, 8 fail on master's lint-gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Measured with LC_ALL=C on the branch after master (#313) was merged in: 79,997 -> 76,259 (-3,738); the old line used pre-#313 figures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Review put #308 on hold over it: the legacy fallback compares .star mtimes, which checkout, sync and the #264 remedy all refresh, so real rises (bundled SEC004-SEC009 included) passed rc 0; and even with star_rules a first-seen rule stayed rc 0 at any count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…n every exec - SEC014 with no external_roles reports nothing on exec; bin/sync-project.sh warns on every sync via new bin/lib/sec014-notice.sh (exact config lines, "none" only on the user's answer recorded in PROJECT.md, re-baseline right after configuring). Fixture tests/wave2/test-sec014-notice.sh (13 cases). - Remaining SEC014 _rule messages (list, blank, unknown role, not FULL) fit lint-gate's 160-char print and name the place to fix it. - CONV009's "all flows at 0 objects" self-check needs activities_for() to find a real object in a zero-count flow, so an early greenfield model is not reported blind (+fixture). - README / TXT002 header: turning a toolkit rule off is `rules: <ID>: enabled: false` in lint-config.yaml (verified on mxcli 0.26.0), never deleting the file; ISO dates are a toolkit opinion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…needs a reason and refuses stock rules (#264 review 2) - The REVERTED path (installer restore and lint-gate BLIND) applies only to MXTK_LINT_RULES_STOCK, the file names `mxcli init` seeds. A headerless toolkit-only rule (ux001, txt*) or configurable conv020 is a local edit: still LOCALLY MODIFIED / configured, never overwritten, not BLIND. The receipt row gains a kind column (stock | toolkit | configurable) so lint-gate, which has no manifest, can tell; rows without one are healed by the next sync and not checked until then. A receipt-listed rule that is missing is still reported for every kind. - One header test in both: anchored `^# mxtk-lint-rule:` (lint-gate used a substring match). - .claude/lint-rules-disabled.txt: each line needs `# reason: …` (a user decision, asked in chat and recorded in PROJECT.md); a line with no reason, or naming a stock-seeded rule, is ignored with a warning by both the installer and lint-gate. The BLIND message no longer suggests opting out. lint-rules/README.md rewritten to match (CONV010 cannot be opted out). - The installer no longer advises adding a project's hash to STOCK-HASHES.txt. - tests/wave2/test-lint-receipt-revert.sh: kinds in the receipt (T0), reasonless and reasoned opt-out (T10), stock opt-out refused (T12), headerless ux001 and configured headerless conv020 not overwritten and not BLIND (T13, T14). 51/51 here; 14 fail with master's installer, 25 with master's installer and lint-gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
… backups, honest restore label (#264 re-review) - Opt-out reason: the installer now accepts `#\s*reason:` like lint-gate (it required `# reason:` exactly, so `#reason: x` was honoured by one tool and ignored by the other). - LINT_RULES_REVERTED_OK=1 no longer gets past the --update-baseline refusal: the override is for looking at counts, and baselining stock counts is how the five-day field case went in. - `.reseeded-<ts>` backups are capped at the newest 3 per rule. - A stock-seeded rule restored because its header is gone and its body matches no known stock hash is labelled "header missing — restored from toolkit, your copy kept as <file>", not "REVERTED TO STOCK": a newer mxcli's seed and a hand edit that lost the header look the same. - tests/wave2/test-lint-receipt-revert.sh: `#reason:` case, override + --update-baseline, backup cap, label. 55/55 here; each new case fails on the previous commit's scripts; 28 fail on master's installer + lint-gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
A pristine scaffold has not chosen its external roles yet, and test-bug12-sync.sh T0 requires a pristine sync to exit 0 with no warning under --strict (61/61 with this change; the warn() version failed T0). Wording aligned in the rule header, README, CHANGELOG and backlog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…ory; SEC014 lint-gate note - Merge origin/fix/lint-receipt-check; README conflict resolved into one "Opting out of a rule": `enabled: false` in lint-config stops reporting (preferred, file stays); .claude/lint-rules-disabled.txt with `# reason:` stops installing toolkit rules and refuses stock ones (conv009 added to that list); never delete the file. TXT002 header and every rule's "remove this rule deliberately" suggestion say the same. - project-bin/lint-gate.sh: one uncounted stderr note while SEC014 is installed and undecided (no exit-code change, no lint row). - bin/lib/sec014-notice.sh and lint-gate count external_roles / enabled: false only under the SEC014 key. test-sec014-notice.sh 24 cases. - SEC014 enabled: false documented as the same user decision as "none". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
MendixMau
pushed a commit
that referenced
this pull request
Oct 13, 2026
PR #312 makes lint-gate.sh print a non-blocking `note: SEC014 not configured` on stderr. run_lint_gate captured that output and showed it only on failure, so the note never appeared on a clean exec. Always echo that one line; exit code unchanged. Fixture: stub lint-gate with the note on stderr and rc 0 -> exec output shows it (fails on the prior head). Refs #287 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
3 of 4 tasks
MendixMau
added a commit
that referenced
this pull request
Oct 14, 2026
) Every constituent PR was reviewed by a fresh agent that did not write it (CLAUDE.md "Merging a PR"); only MERGE verdicts joined. CI green on each constituent head and on this head (40814fc); full suites run on the branch. Review lines (per merged PR): - #315 context trim — MERGE; 360/385 removed lines found verbatim in their new homes, rest read by hand; baseline 79,997 -> 76,259. - #316 independent-review + word-budget rules — MERGE-AFTER-FIX (3 rounds), fixes applied as specified. - #318 page-fidelity displayed text — MERGE after fix round + confirmation. - #317 interaction-model skill (#291 part 1) — MERGE after fix round + confirmation; no client names leaked. - #321 PROOF-OF-LOOK per section; final-look not due before derived Stage 6 — MERGE after fix round + confirmation. - #307 graph-sweep guard; per-BRD conformance ledgers — MERGE after two fix rounds + confirmation. - #325 extraction-quality section; token-burn fixes — MERGE after fix round + confirmation. - #310 e2e OQL by alias, negative-span liveness, APP-DOWN, #302 credential regression fix — MERGE after fix round + 3 confirmations. - #323 inbox promotions — MERGE after fix round + confirmation. - #308 lint gate BLIND on reverted stock rules — MERGE (FIRST-SEEN reverted) after two fix rounds + confirmation. - #328 pipefail + grep -q flake — MERGE; 0/30 flakes vs 15/100 on master. Follow-up: run-all resolves bin/lib/ subjects (reviewed, MERGE). - #319 test-stack-up command-line ownership proof — MERGE after fix round + 2 confirmations. - #327 change-intake core + on-demand detail; CRLF-safe change log — MERGE after fix round + confirmation. - #320 follow-ups to #290/#292; Dispatching split out — MERGE after fix round + confirmation. - #326 status delivery rounds (existing-app mode) — MERGE after fix round + confirmation. - #312 lint batch 4: SEC014, toolkit CONV009 — MERGE after review + 2 fix rounds + confirmation; field run on mxcli 0.26. - #324 exec.sh change classes, shared stale-lock takeover, SEC014 note echo — MERGE (joint review); follow-up owed for a takeover race after a mid-takeover crash. - #322 MxTest after-startup guard, mxcli-run pid check — MERGE (joint review). - #314 doctor property-set exit code; sync announces its bound — MERGE (joint review). - #330 review follow-ups (exit 4/5 docs, pipefail, NUL strip) — MERGE-AFTER-FIX, fixes applied; takeover re-check dropped. Field runs are still owed for most instruments in this batch (stated in each PR). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Owner
Author
|
Landed on master as part of the integration squash #329 (commit 31e90ae), after independent review — its Generated by Claude Code |
This was referenced Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#268: SEC014, an external role that can write state, audit or ownership members
Problem. A partner, customer or portal role granted
create, read *, write * … where [owner = CurrentUser]passes every stock rule:Yet that role can set its own record's
Statusthrough the client API, skipping the approval microflow. In the field this showed up only as CONV006 +4, which was re-baselined. A manual review found it five days later.Fix. New rule
lint-rules/sec014_external_role_write.star: warning severity, in the security tier (theSEC*default inlint-gate.sh)..claude/lint-config.yaml:rules: SEC014: options: external_roles: "A, B". A name can be a user role or a module role, qualified or bare.write *);Enumerationattribute whose name ends in Status, State, Result, Stage or Phase;…At/…Byaudit attribute;Configuration states. These changed after review; see "Unset option: why it is not blocking" below.
"none", and the re-baseline step"A, B""none"None), blank, an unknown role, or a catalog that is not FULL_rulefinding (PASS-BLIND); the message is under 160 characters and names.claude/lint-config.yamlorREFRESH CATALOG FULL"none"switches a security rule off. So the README, the rule header, the blank-value_rulemessage and the sync NOTE all say the same thing: it is set only on the user's answer, asked in chat and recorded inPROJECT.md, never to silence a finding.Re-baselining. The first configured run jumps SEC014 from 0 to N, and lint-gate would blame that security-tier rise on whichever script runs next. The README, CHANGELOG and NOTE therefore say to re-baseline right after configuring, once the user has seen the findings:
bin/lint-gate.sh --update-baseline --reason "SEC014 configured" --approved-in-chat. If #308 lands, its FIRST-SEEN handling covers part of this too.Unset option: why it is not blocking. As first pushed, an absent option was a
_rulefinding. That would have made every synced project'sexec.shexit 1 (LINT BLIND) until someone edited a config file, which is the #229 pattern from an unconfigured CONV020. Moving the fix into the message (point 1) does not change that the exec still exits 1. So the undecided state now lives inbin/sync-project.sh, as a NOTE printed on every sync (newbin/lib/sec014-notice.sh).warn().tests/wave2/test-bug12-sync.shT0 requires a pristine scaffold to sync with exit 0 and no warning under--strict. Thewarn()version failed T0; the NOTE version passes 61/61.#267: CONV009 now reads its options
Problem. Stock
conv009_max_microflow_objects.star(0.17.0–0.26.0) hard-codes 15 and never callsget_option(). ThemaxObjects: 20example in the generatedlint-config.yamltherefore does nothing.Fix.
CONV009readsmaxObjectsandcountLoopBodies, and ships through the replace-the-stock path.maxObjects: 20withcountLoopBodies: truematchesmicroflow-preflight.md._rulefinding.activities_for()finds a real object (not a start, end or merge event) in a zero-count flow. An early greenfield model with empty microflows is no longer reported blind (new fixture).STOCK-HASHES.txtgains conv009 for v0.17.0, v0.24.0 and v0.25.0, plus a full v0.26.0 set. I captured them withmxcli initinto empty directories from the officialmxcli-linux-amd64release assets; the same run reproduced every hash already in the file.skills/lint-that-actually-runs.mdnow says thatoptions:reaches only rules that callget_option(), and that a list value arrives asNone.Also: turning a toolkit rule off (TXT002)
The README and the TXT002 header now say that ISO dates are a toolkit opinion, not a Mendix rule. A project whose display standard is not ISO sets
rules: TXT002: enabled: falseinlint-config.yamland records the decision inPROJECT.md. It must never delete the file, because sync re-installs it. Verified on mxcli 0.26.0: a toolkit rule with 1 finding gave 0 once disabled. The README points to #308's.claude/lint-rules-disabled.txtfor stopping installation once that PR lands.Evidence
Unit tests.
tests/lint-rules/test-batch4-rules.sh: 32/32. It fails on master, where the rules are missing and the harness has noget_option. The stock conv009 also fails themaxObjects: 20case. The harness gained mocks forget_option(with mxcli's conversion: a list becomes None),permissions_for,attributes_for,user_rolesandrole_mappings.Field run (official mxcli 0.26.0, scratch copies of two models, catalog FULL):
maxObjects: 20maxObjects: 10maxObjects: 20countLoopBodies_ruleeachassociations(), which does not exist before 0.25.0 and stops the file loading.Checks run
tests/lint-rules/test-batch4-rules.shtests/lint-rules/test-batch3-rules.shtests/lint-rules/test-batch2-rules.shtests/lint-rules/test-batch1-rules.shtests/lint-rules/test-ux-rules.shtests/test-lint-delivery.shtests/wave2/test-stock-hashes.shtests/wave2/test-sec014-notice.shtests/wave2/test-bug12-sync.shtests/wave2/test-dryrun-writes-nothing.shtests/wave2/test-sync-dup-heading-exit.shtests/wave2/test-ritual-slim.shtests/wave2/test-slim-claude-md.shtests/wave2/test-change-log.shcheck-scripts,check-portability,check-no-private-citations,render-routing --check,check-no-client-data(no denylist),check-pr-discipline origin/masterorigin/master (#313) is merged in, with all CHANGELOG lines kept.
Risks
write *on input-only entities. SEC014 flagswrite *even on an entity whose members are all input fields. That is deliberate per the issue; a reviewer can accept it.external_roles, SEC014 checks nothing on exec. The sync NOTE is the only reminder, and it does not fail--strict.Field run: done (above). Status for both rules stays
drafteduntil a second project reports.Re-review round 2 (merges #308)
origin/fix/lint-receipt-check(#308) is merged into this branch. Itslint-rules/README.mdconflict is resolved into a single "Opting out of a rule" section. That opt-out story is now the same everywhere:rules: <ID>: enabled: falsein.claude/lint-config.yamlstops a rule reporting. This is the preferred route. The.starfile stays installed and keeps refreshing; verified on mxcli 0.26.0, where a rule with 1 finding gave 0 once disabled..claude/lint-rules-disabled.txt, one line per rule with a# reason:, stops sync installing a toolkit rule. Stock-seeded rules are refused; conv009 is now on that list with conv010, data_change_microflows and entity_business_key.Where this now appears:
enabled: false, recorded inPROJECT.md).rules: SEC014: enabled: falseis documented as the same user decision asexternal_roles: "none": asked in chat and recorded inPROJECT.md.SEC014 undecided is no longer silent in practice. While SEC014 is installed and has no
external_rolesunder its key,project-bin/lint-gate.shprints one stderr line on every run:note: SEC014 not configured — set .claude/lint-config.yaml rules: SEC014: options: external_roles (ask the user; 'none' is their decision). It changes no exit code and adds no lint row.bin/lib/sec014-notice.shand the lint-gate check both countexternal_roles, orenabled: false, only under the SEC014 key. The same awk is inlined in lint-gate because a project has nobin/lib. Cases covered in the fixture:external_rolesunder another rule, or at top level, does not count;tests/wave2/test-sec014-notice.shnow has 24 cases. They include one note line, no note once configured or when SEC014 is not installed, and the same exit code either way.Known gap (outside my files).
exec.shcaptures lint-gate output and shows it only on a failure, so the note does not appear during a clean exec. The sync NOTE and direct lint-gate runs carry it. A one-lineexec.shfollow-up would surface it (grep '^note: SEC014' "$_lo").Round 2 checks:
tests/lint-rules/test-batch4-rules.shtests/lint-rules/test-batch3-rules.shtests/lint-rules/test-batch2-rules.shtests/lint-rules/test-batch1-rules.shtests/lint-rules/test-ux-rules.shtests/test-lint-delivery.shtests/wave2/test-sec014-notice.shtests/wave2/test-lint-receipt-revert.shtests/wave2/test-stock-hashes.shtests/wave2/test-lint-gate-accept.shcheck-scripts,check-portability,check-no-private-citations,render-routing --check,check-no-client-data,check-pr-disciplineCloses #268
Closes #267
🤖 Generated with Claude Code
https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Generated by Claude Code