kijito-tools 0.2.0
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/LICENSE +201 -0
- package/NOTICE +6 -0
- package/README.md +220 -0
- package/bin/cli.js +34 -0
- package/install.sh +161 -0
- package/package.json +50 -0
- package/providers/_shared/wake-core.mjs +358 -0
- package/providers/claude/CLAUDE.md.snippet +31 -0
- package/providers/claude/install.sh +186 -0
- package/providers/claude/scripts/arm-session.sh +66 -0
- package/providers/claude/scripts/claude-armed.sh +101 -0
- package/providers/claude/scripts/heartbeat-watchdog.sh +209 -0
- package/providers/claude/scripts/inbox-selftest.sh +277 -0
- package/providers/claude/scripts/kijito-persona-lib.sh +94 -0
- package/providers/claude/scripts/kijito-qa-pass.sh +12 -0
- package/providers/claude/scripts/lifecycle-lib.sh +167 -0
- package/providers/claude/scripts/myctx.sh +46 -0
- package/providers/claude/scripts/self-clear.sh +134 -0
- package/providers/claude/scripts/session-autosend.sh +85 -0
- package/providers/claude/scripts/session-catchup-hint.sh +330 -0
- package/providers/claude/scripts/statusline-context.sh +67 -0
- package/providers/claude/skills/kijito-qa-memory/SKILL.md +110 -0
- package/providers/claude/skills/kijito-recall/SKILL.md +53 -0
- package/providers/claude/skills/kijito-start/SKILL.md +122 -0
- package/providers/claude/wiring/README.md +93 -0
- package/providers/claude/wiring/kijito-heartbeat@.service +21 -0
- package/providers/codex/README.md +75 -0
- package/providers/codex/codex-kijito-parity-plan.md +265 -0
- package/providers/codex/docs-codex-setup.md +64 -0
- package/providers/codex/install.mjs +184 -0
- package/providers/codex/n0-capability-probe-protocol.md +472 -0
- package/providers/codex/n0-harness/README.md +57 -0
- package/providers/codex/n0-harness/cli.mjs +58 -0
- package/providers/codex/n0-harness/evidence-manifest.mjs +77 -0
- package/providers/codex/n0-harness/fixture.mjs +279 -0
- package/providers/codex/n0-harness/lib.mjs +141 -0
- package/providers/codex/n0-harness/manifest.mjs +36 -0
- package/providers/codex/n0-harness/oracle.mjs +464 -0
- package/providers/codex/n0-harness/parser.mjs +200 -0
- package/providers/codex/n0-harness/prompt.mjs +43 -0
- package/providers/codex/n0-harness/snapshot.mjs +120 -0
- package/providers/codex/n0-harness/specimen.mjs +37 -0
- package/providers/codex/notify/kijito-notify-count.mjs +117 -0
- package/providers/codex/notify/kijito-notify-count.test.mjs +76 -0
- package/providers/codex/plans/gate4-battery-log.md +184 -0
- package/providers/codex/plans/gate4-battery-protocol.md +43 -0
- package/providers/codex/plans/hive-user-first-plan-QA.md +34 -0
- package/providers/codex/plans/hive-user-first-plan.md +247 -0
- package/providers/codex/release-manifest.json +53 -0
- package/providers/codex/same-chat-continuation-plan-gate.md +235 -0
- package/providers/codex/same-chat-continuation-plan.md +458 -0
- package/providers/codex/skills/kijito-qa-memory/SKILL.md +193 -0
- package/providers/codex/skills/kijito-qa-memory/agents/openai.yaml +4 -0
- package/providers/codex/skills/kijito-recall/SKILL.md +53 -0
- package/providers/codex/skills/kijito-start/SKILL.md +173 -0
- package/providers/codex/skills/kijito-start/agents/openai.yaml +4 -0
- package/providers/codex/test/n0-cli-refusal.test.mjs +31 -0
- package/providers/codex/test/n0-counterexample-matrix.test.mjs +118 -0
- package/providers/codex/test/n0-guard-census-core.mjs +1372 -0
- package/providers/codex/test/n0-guard-census.json +35479 -0
- package/providers/codex/test/n0-guard-census.mjs +31 -0
- package/providers/codex/test/n0-guard-census.test.mjs +374 -0
- package/providers/codex/test/n0-guard-counterexamples.mjs +1068 -0
- package/providers/codex/test/n0-guard-independent-projection.mjs +70 -0
- package/providers/codex/test/n0-guard-independent-projection.test.mjs +53 -0
- package/providers/codex/test/n0-guard-manifest-author.mjs +424 -0
- package/providers/codex/test/n0-guard-mutation-operators.test.mjs +244 -0
- package/providers/codex/test/n0-guard-mutation-runner.mjs +599 -0
- package/providers/codex/test/n0-guard-remainder-classes.test.mjs +314 -0
- package/providers/codex/test/n0-guard-sanitizer-pairs.test.mjs +68 -0
- package/providers/codex/test/n0-guard-shared-context-predicates.test.mjs +84 -0
- package/providers/codex/test/n0-harness.test.mjs +553 -0
- package/providers/codex/test/n0-notable-regressions.test.mjs +70 -0
- package/providers/codex/test/n0-subject-boundary.mjs +97 -0
- package/providers/codex/test/n0-subject-boundary.test.mjs +72 -0
- package/providers/codex/test/same-chat-plan-preflight.sh +167 -0
- package/providers/codex/tools/refresh-manifest.mjs +81 -0
- package/providers/codex/wake-helper/TRANSPORT-NOTES.md +25 -0
- package/providers/codex/wake-helper/integration.test.mjs +285 -0
- package/providers/codex/wake-helper/kijito-wake-helper.mjs +488 -0
- package/providers/codex/wake-helper/kijito-wake-helper.test.mjs +128 -0
- package/providers/codex/wake-helper/mock-daemon.mjs +64 -0
- package/providers/codex/wake-helper/status-probe.mjs +45 -0
- package/providers/codex/wake-helper/ws-uds.mjs +153 -0
- package/providers/monitor/.github/workflows/publish-npm.yml +38 -0
- package/providers/monitor/.github/workflows/publish-pypi.yml +30 -0
- package/providers/monitor/CHANGELOG.md +465 -0
- package/providers/monitor/IMPORT-PROVENANCE.md +68 -0
- package/providers/monitor/LICENSE +202 -0
- package/providers/monitor/NOTICE +7 -0
- package/providers/monitor/OPAQUE-OUTPUT-ENFORCEMENT.md +45 -0
- package/providers/monitor/README.md +571 -0
- package/providers/monitor/RELEASING.md +125 -0
- package/providers/monitor/arm-hive-monitor.sh +13 -0
- package/providers/monitor/bin/cli.js +43 -0
- package/providers/monitor/com.kijito.inbox-monitor.plist.template +59 -0
- package/providers/monitor/docs/DESIGN.md +984 -0
- package/providers/monitor/kijito-inbox-monitor@.service.template +95 -0
- package/providers/monitor/kijito_inbox_monitor.py +3666 -0
- package/providers/monitor/package.json +35 -0
- package/providers/monitor/pyproject.toml +42 -0
- package/providers/monitor/scripts/mutation-check.py +399 -0
- package/providers/monitor/scripts/prepublish-gate.sh +284 -0
- package/providers/monitor/test_kijito_monitor.py +5609 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Pre-publish gates for a release. Run from anywhere in the repo; see RELEASING.md step 3.
|
|
3
|
+
#
|
|
4
|
+
# ./scripts/prepublish-gate.sh # canary, then the gates over the public surface
|
|
5
|
+
# ./scripts/prepublish-gate.sh --canary # prove the gate can still fail, then exit
|
|
6
|
+
#
|
|
7
|
+
# Four checks over the PUBLIC surface (this count is the exact thing that drifts - if you add a check,
|
|
8
|
+
# the loops below and RELEASING.md step 3 say it too):
|
|
9
|
+
# 1. typography - em/en dashes, curly quotes, ellipsis
|
|
10
|
+
# 2. memory-ids - internal Kijito memory ids ([[12345]] / [12345])
|
|
11
|
+
# 3. path-escapes - references to paths ABOVE the repository root (../)
|
|
12
|
+
# 4. private-detail - an operator absolute home path, or a private handoff sentinel
|
|
13
|
+
#
|
|
14
|
+
# Why check 3 exists (added 2026-07-29, after finding two live instances): RELEASING.md instructed the
|
|
15
|
+
# reader to run `../bin/producer-health.sh`, a helper that lives in the private workspace ALONGSIDE this
|
|
16
|
+
# repo and is not part of the package. A clone therefore could not run the gate its own release document
|
|
17
|
+
# mandated, and `../bin/...` would resolve to whatever happened to sit above the checkout - so the
|
|
18
|
+
# instruction was not merely broken, it was ambiguous in a way that depends on the reader's directory
|
|
19
|
+
# layout. The second instance was a stale "still open" note in docs/DESIGN.md pointing at a repo-external
|
|
20
|
+
# copy of itself. Both are the same class: THE PUBLIC SURFACE DESCRIBING THINGS THAT ARE NOT IN IT.
|
|
21
|
+
# NOTE ON SCOPE: this bans `../` across the whole surface, code included. There is no legitimate use
|
|
22
|
+
# today. If one ever arises - a JS require, say - that should be a deliberate, visible decision at this
|
|
23
|
+
# gate, not a quietly loosened regex.
|
|
24
|
+
#
|
|
25
|
+
# Properties this script exists to guarantee, each of which has failed in practice:
|
|
26
|
+
#
|
|
27
|
+
# * THE FILE LIST IS RE-DERIVED from `git ls-files`, never hardcoded. A hardcoded list has been
|
|
28
|
+
# wrong twice, and it silently omits exactly the files added since someone last edited it.
|
|
29
|
+
# pyproject.toml and package.json are included deliberately: they carry the PyPI/npm
|
|
30
|
+
# descriptions, which are IMMUTABLE per version and so cannot be fixed after publishing.
|
|
31
|
+
#
|
|
32
|
+
# * PATHS ARE NUL-DELIMITED into `xargs -0`. Writing this inline as
|
|
33
|
+
# FILES=$(git ls-files); grep -nE ... $FILES || echo clean
|
|
34
|
+
# does not word-split in zsh: grep gets one nonexistent filename, exits non-zero, and the
|
|
35
|
+
# `|| echo clean` prints success having inspected NOTHING. That false clean was observed here.
|
|
36
|
+
#
|
|
37
|
+
# * IT NAMES ITS SPECIMEN AND REFUSES A FOREIGN ONE (assay review, 2026-07-30). `git rev-parse
|
|
38
|
+
# --show-toplevel` resolves to whatever repository you happen to be standing in, so run from
|
|
39
|
+
# somewhere else this script cheerfully gated ANOTHER project and printed GATES CLEAN. A pass that
|
|
40
|
+
# does not say what it inspected is not evidence, and a gate that can pass over the wrong subject
|
|
41
|
+
# is an instance of the very wrong-specimen class it was written to catch.
|
|
42
|
+
#
|
|
43
|
+
# * THE CANARY RUNS IN BOTH DIRECTIONS, ONE SHAPE AT A TIME (assay review, 2026-07-30). A gate is
|
|
44
|
+
# only evidence if it can still fail. Two refinements over the original, both learned the hard way:
|
|
45
|
+
# (1) each bad SHAPE is checked ALONE, because a combined fixture proves only that SOME line
|
|
46
|
+
# matched - a pattern narrowed to the one historical string (`\.\./bin/`) would still have passed
|
|
47
|
+
# while missing every other escape; (2) the patterns must also stay SILENT on a good fixture that
|
|
48
|
+
# deliberately CONTAINS the near-miss shapes they must ignore, because a detector sabotaged to flag
|
|
49
|
+
# EVERYTHING passes any bad-input-only canary. A canary can only see the variable it moves.
|
|
50
|
+
set -eu
|
|
51
|
+
|
|
52
|
+
usage() {
|
|
53
|
+
cat <<'USAGE'
|
|
54
|
+
usage: prepublish-gate.sh [--canary]
|
|
55
|
+
|
|
56
|
+
(no args) run the canary, then the gates over the public surface
|
|
57
|
+
--canary run the canary only (prove the gate can still fail), then exit
|
|
58
|
+
-h, --help this text
|
|
59
|
+
|
|
60
|
+
exit: 0 clean | 1 a gate FAILED | 2 the gate could not be trusted (bad args, wrong repo, broken canary)
|
|
61
|
+
USAGE
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# REAL ARGUMENT HANDLING (assay review, 2026-07-30). This used to accept and silently IGNORE anything,
|
|
65
|
+
# so `--canary` - the flag every other check in this fleet has - ran the FULL gate and reported clean.
|
|
66
|
+
# A tool that ignores an argument you meant is worse than one that rejects it: you believe you ran
|
|
67
|
+
# something you did not.
|
|
68
|
+
canary_only=0
|
|
69
|
+
for arg in "$@"; do
|
|
70
|
+
case "$arg" in
|
|
71
|
+
--canary) canary_only=1 ;;
|
|
72
|
+
-h|--help) usage; exit 0 ;;
|
|
73
|
+
*) printf 'ABORT: unknown argument: %s\n\n' "$arg" >&2; usage >&2; exit 2 ;;
|
|
74
|
+
esac
|
|
75
|
+
done
|
|
76
|
+
|
|
77
|
+
# THE PIPE WARNING, AT RUN TIME, ON STDERR (the 3b339ce fleet standard). A header comment is read by
|
|
78
|
+
# whoever EDITS the script, not by whoever RUNS it - and running it is when the mistake happens. stderr
|
|
79
|
+
# does not travel down the pipe, so this still lands on the terminal in the exact case being warned about.
|
|
80
|
+
if [ ! -t 1 ]; then
|
|
81
|
+
printf '%s\n' "note: stdout is not a terminal. If you piped this, \$? is the LAST pipeline stage's status, NOT this gate's answer. Use \${PIPESTATUS[0]} (bash) / \${pipestatus[1]} (zsh), or run it unpiped." >&2
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
TYPOGRAPHY='—|–|[“”‘’]|…'
|
|
85
|
+
# MEMORY IDS — DECLARED NARROWING, not an exemption. Read the distinction, it is the whole point:
|
|
86
|
+
# an EXEMPTION skips a SURFACE entirely (nothing on it is checked, by anything); a NARROWING drops one
|
|
87
|
+
# ambiguous PATTERN CLASS while every other check still runs over that same surface.
|
|
88
|
+
#
|
|
89
|
+
# ⚠️ THIS USED TO ALSO MATCH THE BARE `[12345]` FORM, and that is why `test_*` and `.github/` were
|
|
90
|
+
# EXEMPT: `test_kijito_monitor.py` contains `self.assertEqual(em.new_ids, [1200])` and friends, which
|
|
91
|
+
# are Python integer-list literals and not memory ids. The bare form is indistinguishable from a list
|
|
92
|
+
# literal ANYWHERE, so the old shape forced a choice between false positives and skipping whole
|
|
93
|
+
# published files — and it chose the second, on a stated reason that turned out to be false (see the
|
|
94
|
+
# SCOPE block below). Narrowing the PATTERN removes the need to skip the SURFACE.
|
|
95
|
+
# ⇒ COST, STATED RATHER THAN BURIED: a bare `[12345]` memory id written in prose is no longer caught
|
|
96
|
+
# here. Every real leak this row was opened for used the `[[...]]` form (`[[22206]]` in
|
|
97
|
+
# scripts/mutation-check.py), and the private-detail check independently catches the sentinel.
|
|
98
|
+
#
|
|
99
|
+
# 🛑 THE SAME NARROWING IS DECLARED IN River's `gates/public-remote-leak-gate.sh`, AND THE DUPLICATION
|
|
100
|
+
# IS DELIBERATE. Sharing one literal between them would make that gate's INSTRUMENT depend on this
|
|
101
|
+
# repo — "a gate may reach outside its repo for its SUBJECT, never for its INSTRUMENT" — so a single
|
|
102
|
+
# edit here would silently redefine what the external check is able to see. Two independent copies
|
|
103
|
+
# that can drift are strictly safer than one copy that can be moved from the subject side.
|
|
104
|
+
MEMORY_IDS='\[\[[0-9]+\]\]'
|
|
105
|
+
PATH_ESCAPES='\.\./'
|
|
106
|
+
# PRIVATE DETAIL (M185): an operator's absolute home directory, or a private Kijito handoff sentinel,
|
|
107
|
+
# published to a public remote. Both were live here: the tracked LaunchAgent baked ONE operator's home
|
|
108
|
+
# into seven paths, and RELEASING.md named a private current-state sentinel.
|
|
109
|
+
# ★ MATCHED BY CONTENT, NEVER BY FILENAME - the M160 lesson. A filename-based rule ("check the plist")
|
|
110
|
+
# is a rule about the files you already thought of; the leak arrives in the file you did not.
|
|
111
|
+
PRIVATE_DETAIL='/(Users|home)/[A-Za-z0-9._-]+|_CURRENT_STATE_POINTER'
|
|
112
|
+
|
|
113
|
+
# THE SCOPE. EVERYTHING TRACKED IS INSPECTED, MINUS EXACTLY ONE ENUMERATED FILE:
|
|
114
|
+
# scripts/prepublish-gate.sh - THIS FILE, and the only one that HAS to be exempt: it necessarily
|
|
115
|
+
# CONTAINS the literals it bans, because they are its patterns and its own
|
|
116
|
+
# documentation. Any other way of sparing them would mean weakening the patterns.
|
|
117
|
+
#
|
|
118
|
+
# ⚠️ IT USED TO EXEMPT THE WHOLE `scripts/` PREFIX, and that was a silent scope hole (assay's review,
|
|
119
|
+
# 2026-07-30 - M184). Narrowing it to the one file that needs it immediately surfaced TWO REAL LEAKS
|
|
120
|
+
# that had been invisible the whole time: internal Kijito memory ids in `scripts/mutation-check.py`,
|
|
121
|
+
# published to a public remote. A prefix is a cheap way to SPELL an exemption and an expensive way to
|
|
122
|
+
# MEAN one - it silently covers every file added under that prefix later, forever.
|
|
123
|
+
#
|
|
124
|
+
# 🛑🛑 AND IT ALSO EXEMPTED `test_`, `tests/` AND `.github/` UNTIL 2026-08-01, ON A STATED REASON THAT
|
|
125
|
+
# WAS SIMPLY FALSE. The comment read "the suite is not published surface" and "CI configuration is not
|
|
126
|
+
# published surface". Measured on a fresh bare clone of the PUBLIC remote:
|
|
127
|
+
# .github/workflows/publish-npm.yml
|
|
128
|
+
# .github/workflows/publish-pypi.yml
|
|
129
|
+
# test_kijito_monitor.py
|
|
130
|
+
# All three are tracked; every `git clone` hands a stranger all three; all three were skipped by ALL
|
|
131
|
+
# FOUR checks. ⇒ ★ AN EXEMPTION CAN BE WRONG IN ITS PREMISE RATHER THAN ITS FORM. Enumerating those
|
|
132
|
+
# files by name and canarying every direction would have left a well-formed exemption resting on a
|
|
133
|
+
# claim that was not true. CHECK WHETHER THE STATED REASON IS TRUE BEFORE ARGUING ABOUT THE SHAPE.
|
|
134
|
+
#
|
|
135
|
+
# ⚖️ WHY THEY WERE DROPPED RATHER THAN RE-JUSTIFIED (assay's ruling, 2026-08-01). The tempting fix was
|
|
136
|
+
# to keep them with a TRUE reason - "inspected by River's gate_M185 instead", which is factually
|
|
137
|
+
# correct, since that checker covers the whole published surface minus one enumerated path. It was
|
|
138
|
+
# REJECTED on a property neither option named: TIMING. This gate is PREVENTIVE - it runs before
|
|
139
|
+
# content reaches the remote. gate_M185 is DETECTIVE - it measures the remote after the fact. A true
|
|
140
|
+
# justification that routes a surface to the detective control silently converts prevention into
|
|
141
|
+
# post-publish detection: a leak in tests or CI would publish FIRST and be found SECOND, which is the
|
|
142
|
+
# wrong order for the only control class whose entire value is running first.
|
|
143
|
+
# ⇒ The false-positive problem that motivated the original exemption is solved by the DECLARED
|
|
144
|
+
# NARROWING on MEMORY_IDS above, not by skipping files: typography, path-escapes and private-detail
|
|
145
|
+
# now run UNNARROWED over tests and CI, because nothing in those classes collides there.
|
|
146
|
+
EXEMPT='^(scripts/prepublish-gate\.sh$)'
|
|
147
|
+
|
|
148
|
+
fixture=$(mktemp)
|
|
149
|
+
trap 'rm -f "$fixture"' EXIT
|
|
150
|
+
|
|
151
|
+
# ---- canary, direction 1: every bad SHAPE must fire, each checked ALONE ---------------------------
|
|
152
|
+
bad_shape() { # label, pattern, shape
|
|
153
|
+
printf '%s\n' "$3" > "$fixture"
|
|
154
|
+
if ! grep -qE "$2" "$fixture"; then
|
|
155
|
+
printf 'ABORT: %s does NOT fire on a known-bad shape, so a clean result from it means nothing:\n %s\n' "$1" "$3" >&2
|
|
156
|
+
exit 2
|
|
157
|
+
fi
|
|
158
|
+
}
|
|
159
|
+
bad_shape typography "$TYPOGRAPHY" 'an em — dash'
|
|
160
|
+
bad_shape typography "$TYPOGRAPHY" 'an en – dash'
|
|
161
|
+
bad_shape typography "$TYPOGRAPHY" 'curly “double” quotes'
|
|
162
|
+
bad_shape typography "$TYPOGRAPHY" 'curly ‘single’ quotes'
|
|
163
|
+
bad_shape typography "$TYPOGRAPHY" 'an ellipsis… character'
|
|
164
|
+
bad_shape memory-ids "$MEMORY_IDS" 'a wikilink [[12345]] id'
|
|
165
|
+
bad_shape memory-ids "$MEMORY_IDS" 'the real historical leak [[22206]] shape'
|
|
166
|
+
bad_shape path-escapes "$PATH_ESCAPES" ' ../bin/some-helper.sh'
|
|
167
|
+
bad_shape path-escapes "$PATH_ESCAPES" 'require("../lib/thing")'
|
|
168
|
+
bad_shape path-escapes "$PATH_ESCAPES" '../../etc/somewhere'
|
|
169
|
+
bad_shape path-escapes "$PATH_ESCAPES" 'see ../docs/DESIGN.md'
|
|
170
|
+
bad_shape private-detail "$PRIVATE_DETAIL" ' <string>/Users/someone/Code/thing/x.py</string>'
|
|
171
|
+
bad_shape private-detail "$PRIVATE_DETAIL" 'a linux home /home/someone/.config/x'
|
|
172
|
+
bad_shape private-detail "$PRIVATE_DETAIL" 'see the pointer, sentinel SOMEONE_CURRENT_STATE_POINTER_V1'
|
|
173
|
+
|
|
174
|
+
# ---- canary, direction 2: the GOOD fixture CONTAINS what each pattern must ignore -----------------
|
|
175
|
+
# It is not enough for the good fixture to merely OMIT the bad shapes - a detector sabotaged to flag
|
|
176
|
+
# everything passes that. Every line below is a near miss the gate must stay silent about.
|
|
177
|
+
cat > "$fixture" <<'GOOD'
|
|
178
|
+
an ascii - hyphen, "straight" double and 'straight' single quotes, and three dots...
|
|
179
|
+
short ids [12] [123], a longer one [123456], plus [[notdigits]] and [abc]
|
|
180
|
+
the DECLARED NARROWING, pinned: bare [1234] and [12345] and self.assertEqual(em.new_ids, [1200])
|
|
181
|
+
./relative/path, file..txt, three...dots, /absolute/path, parent/child
|
|
182
|
+
~/.config/kijito-inbox-monitor/token and $HOME/.cache/x and __HOME__/.cache/y
|
|
183
|
+
/usr/local/bin, /opt/homebrew/bin/python3, /etc/hosts, /var/log/x
|
|
184
|
+
the operator current state pointer, written as lower-case prose
|
|
185
|
+
GOOD
|
|
186
|
+
for label_pattern in "typography:$TYPOGRAPHY" "memory-ids:$MEMORY_IDS" "path-escapes:$PATH_ESCAPES" "private-detail:$PRIVATE_DETAIL"; do
|
|
187
|
+
label=${label_pattern%%:*}
|
|
188
|
+
pattern=${label_pattern#*:}
|
|
189
|
+
if grep -qE "$pattern" "$fixture"; then
|
|
190
|
+
printf 'ABORT: %s fires on known-GOOD input, so it would condemn a release regardless of content:\n' "$label" >&2
|
|
191
|
+
grep -nE "$pattern" "$fixture" >&2
|
|
192
|
+
exit 2
|
|
193
|
+
fi
|
|
194
|
+
done
|
|
195
|
+
# ---- canary, direction 3: the SCOPE, not the patterns (M184) --------------------------------------
|
|
196
|
+
# A pattern that works proves nothing about a file the gate never opens. Two of the three checks above
|
|
197
|
+
# were passing for years while `scripts/` was wholly exempt and leaking. So: assert that a leak planted
|
|
198
|
+
# under scripts/ WOULD be inspected, and that the one file spared is spared by an exemption this script
|
|
199
|
+
# DECLARES. Silence about a skipped path is the failure, not the skipping.
|
|
200
|
+
# ⚠️ THE FORMERLY-EXEMPT PREFIXES ARE PLANTED HERE DELIBERATELY. Until 2026-08-01 not one direction
|
|
201
|
+
# in this loop touched `test_`, `tests/` or `.github/` - so the three prefixes whose justification was
|
|
202
|
+
# false were also the three no control ever exercised. A canary that never touches the contested
|
|
203
|
+
# surface is the defect this whole finding started from, so each of them now has a planted path.
|
|
204
|
+
for planted in "scripts/leak-canary.md" "scripts/mutation-check.py" "docs/DESIGN.md" "README.md" \
|
|
205
|
+
"test_kijito_monitor.py" "tests/test_something.py" ".github/workflows/publish-npm.yml"; do
|
|
206
|
+
if printf '%s\n' "$planted" | grep -qE "$EXEMPT"; then
|
|
207
|
+
printf 'ABORT: %s would be EXEMPT from every check, so a leak planted there is invisible.\n' "$planted" >&2
|
|
208
|
+
exit 2
|
|
209
|
+
fi
|
|
210
|
+
done
|
|
211
|
+
if ! printf '%s\n' "scripts/prepublish-gate.sh" | grep -qE "$EXEMPT"; then
|
|
212
|
+
printf 'ABORT: the gate no longer exempts ITSELF, so it will condemn its own pattern literals.\n' >&2
|
|
213
|
+
exit 2
|
|
214
|
+
fi
|
|
215
|
+
echo "canary: every pattern fires on each known-bad shape and stays silent on known-good input"
|
|
216
|
+
echo "canary: scope holds - a leak planted under scripts/, tests/, test_* or .github/ is inspected;"
|
|
217
|
+
echo " only scripts/prepublish-gate.sh is exempt, and it is declared"
|
|
218
|
+
|
|
219
|
+
if [ "$canary_only" -eq 1 ]; then
|
|
220
|
+
echo "CANARY CLEAN - the gate can still tell good from bad. (No surface inspected: --canary.)"
|
|
221
|
+
exit 0
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# ---- name the specimen, and refuse a foreign one --------------------------------------------------
|
|
225
|
+
root=$(git rev-parse --show-toplevel 2>/dev/null) || {
|
|
226
|
+
echo "ABORT: not inside a git repository, so there is no surface to gate." >&2; exit 2; }
|
|
227
|
+
cd "$root"
|
|
228
|
+
if [ ! -f kijito_inbox_monitor.py ] || [ ! -f RELEASING.md ]; then
|
|
229
|
+
printf 'ABORT: %s is not the kijito-inbox-monitor repository (no kijito_inbox_monitor.py + RELEASING.md).\n' "$root" >&2
|
|
230
|
+
printf ' Refusing to report a clean gate over a repository this gate was not written for.\n' >&2
|
|
231
|
+
exit 2
|
|
232
|
+
fi
|
|
233
|
+
echo "specimen: $root"
|
|
234
|
+
|
|
235
|
+
# --cached AND --others, because `git ls-files` alone reproduces one level up the very defect the
|
|
236
|
+
# comment at the top of this file warns about. That comment says a HARDCODED list "silently omits
|
|
237
|
+
# exactly the files added since someone last edited it", and re-deriving from git fixed that - for
|
|
238
|
+
# TRACKED files. An untracked new file is still omitted, and the gate still printed "GATES CLEAN".
|
|
239
|
+
# ⇒ MEASURED 2026-08-06: a new, unstaged kijito-inbox-monitor@.service.template gave
|
|
240
|
+
# "surface: 17 file(s) -> GATES CLEAN" while that file was never opened; `git add` made it 18.
|
|
241
|
+
# ★ A BRAND-NEW FILE IS THE LIKELIEST ONE TO CARRY A LEAK - nobody has ever reviewed it - and it was
|
|
242
|
+
# precisely the one the gate could not see. Silence read as approval.
|
|
243
|
+
# --exclude-standard keeps .gitignore'd build artifacts (build/, *.egg-info/, __pycache__/) out, so
|
|
244
|
+
# this widens the surface to unreviewed SOURCE only, never to generated noise.
|
|
245
|
+
tracked=$(git ls-files -z --cached --others --exclude-standard | tr '\0' '\n' | sort -u)
|
|
246
|
+
files=$(printf '%s\n' "$tracked" | grep -vE "$EXEMPT" || true)
|
|
247
|
+
excluded=$(printf '%s\n' "$tracked" | grep -E "$EXEMPT" || true)
|
|
248
|
+
if [ -z "$files" ]; then
|
|
249
|
+
echo "ABORT: re-derived file list is EMPTY - refusing to report a clean gate over nothing." >&2
|
|
250
|
+
exit 2
|
|
251
|
+
fi
|
|
252
|
+
count=$(printf '%s\n' "$files" | wc -l | tr -d ' ')
|
|
253
|
+
echo "surface: $count file(s)"
|
|
254
|
+
# DECLARE THE EXEMPTIONS. Silence about what was skipped is the failure mode; a reader must be able to
|
|
255
|
+
# tell "clean" from "not looked at" without reading this script.
|
|
256
|
+
if [ -n "$excluded" ]; then
|
|
257
|
+
printf 'exempt: %s file(s) NOT inspected -' "$(printf '%s\n' "$excluded" | wc -l | tr -d ' ')"
|
|
258
|
+
printf ' %s' $(printf '%s\n' "$excluded")
|
|
259
|
+
printf '\n'
|
|
260
|
+
else
|
|
261
|
+
echo "exempt: none"
|
|
262
|
+
fi
|
|
263
|
+
# DECLARE THE NARROWING AT RUN TIME, beside the exemptions. It is a real reduction in what this gate
|
|
264
|
+
# can see, and a reduction that lives only in a source comment is one the reader never learns about
|
|
265
|
+
# at the moment they are deciding whether to trust a clean result.
|
|
266
|
+
echo "narrowed: memory-ids matches the unambiguous [[nnnnn]] form only - the bare [nnnnn] form is"
|
|
267
|
+
echo " indistinguishable from an integer list literal (e.g. new_ids, [1200]) and is NOT checked"
|
|
268
|
+
|
|
269
|
+
status=0
|
|
270
|
+
for label_pattern in "typography:$TYPOGRAPHY" "memory-ids:$MEMORY_IDS" "path-escapes:$PATH_ESCAPES" "private-detail:$PRIVATE_DETAIL"; do
|
|
271
|
+
label=${label_pattern%%:*}
|
|
272
|
+
pattern=${label_pattern#*:}
|
|
273
|
+
hits=$(printf '%s\n' "$files" | tr '\n' '\0' | xargs -0 grep -nE "$pattern" || true)
|
|
274
|
+
if [ -n "$hits" ]; then
|
|
275
|
+
echo "FAIL $label:"
|
|
276
|
+
printf '%s\n' "$hits"
|
|
277
|
+
status=1
|
|
278
|
+
else
|
|
279
|
+
echo "pass $label"
|
|
280
|
+
fi
|
|
281
|
+
done
|
|
282
|
+
|
|
283
|
+
[ "$status" -eq 0 ] && echo "GATES CLEAN over $count file(s) in $root" || echo "GATES FAILED - do not tag"
|
|
284
|
+
exit "$status"
|