Preserve complete source ancestry for PRs #3512, #3513, #3515, #3516, #3517 and #3518. Repair bounded recovery, alias compatibility and evaluator packaging gaps while keeping registry reuse inside the canonical loader. Verified actual hook, setup, dashboard, evaluator, sandbox and profile workflows plus full npm, Python, lint and supply-chain checks. ECC-043 preparation only; integration remains with the delivery owner.
A real CLI fixture reproduced an infinite allocation loop from an existing unsafe numeric ID. Reject unsafe IDs and overflow without writing markers; 26 workflow fixtures, changed-code coverage and validators pass.
Preserve full contributor head; refresh catalogs and Pi projection. Verify disposable Claude/Codex installs and resource discovery, with no automatic hooks or governance files.
Preserve full contributor history and current catalog. Actual process workflows cover concurrency, recovery, sprint planning and read-only status. Bound registry symlinks, counts and lock waits; preserve manually curated trigger provenance.
- questionIdsFor and condensedHintFor take an optional
{ externalContract: true }; without it a code change that handles data
asks data-schema only, and an unknown profile gets the original four
questions and condensed hint.
- The hook reads GATEGUARD_EXTERNAL_CONTRACT_QUESTION with the same
spellings as GATEGUARD_METRICS and passes it to the full denial, the
condensed denial and the metrics record, so all three agree.
- The effectiveness harness's arm patch forwards the option, so an arm
never drops it silently.
Docs: change-profile.md gains an External-contract question section and
qualifies "byte-identical to the fixed four questions"; design-notes.md,
SKILL.md (switch table, data rule, word-for-word set) and CHANGELOG say
the fifth question is opt-in; question-effectiveness.md and the harness
README note that the runner clears GATEGUARD_* variables, so a gate arm
from a current tree measures the gate without the question.
skill-triggers@1.json: registryDigest refreshed for the SKILL.md body
change; the description and triggers are unchanged.
- design-notes.md: new Type filters and Path patterns sections; Search
scope extends the search-of-the-target rule to name lookups; Flag
parsing lists the new value flags.
- change-profile.md: Unicode line terminators make an edit non-trivial.
- skills/gateguard/SKILL.md and CHANGELOG.md describe the same rules.
- skill-triggers@1.json: refresh registryDigest for the SKILL.md body
change. The skill's description is unchanged, so triggers and
triggersDigest are untouched, as in the plan-orchestrate refresh.
Drop the duplicate ECC_PREPUSH_RUN_CHECKS key now that main sets it,
refresh the skill-triggers registry digest, classify skill-host-compat
for pi/core, and bump the Turkish AGENTS.md counts the catalog sync
does not cover.
Resolve six conflicts:
- hooks/hooks.metadata.json: keep this branch's gateguard description and
fingerprint, add upstream's hookify-runtime entries.
- package.json: keep both gateguard-report.js and github-coordination.js.
- scripts/hooks/gateguard-fact-force.js, tests/hooks/gateguard-fact-force.test.js,
skills/gateguard/SKILL.md: upstream added a simpler session denial cap; this
branch already has the fuller one (cap threaded through
markCheckedAndCountDenial, malformed-value warning, miss and target notes),
so keep it. Upstream's taskset and su wrapper handling, fail-closed
recursion limit and soft-hyphen sanitising merged cleanly and their tests pass.
- manifests/context-packs/skill-triggers@1.json: take upstream's file and
recompute registryDigest for the merged registry.
Treat non-2xx activity responses as feed errors, preserve non-Markdown Copilot agent assets, and apply the reviewed reference escape parity correction with regenerated bindings. Allow the test runner to drain piped output on its existing success or failure exit status. Validate installed workflows and bounded real Node20/24 pipe regressions.
Resolve the one conflict, skill-triggers@1.json: take upstream's
pretty-printed manifest and recompute registryDigest for the merged
registry so validate-context-profiles passes.
A non-numeric, negative, or exponent Points value was persisted and
silently skewed the derived sprint total (bogus -> 0, -3, 1e3 -> 1000)
with no drift reported. parseStory now enforces the documented values,
so apply rejects the batch and status reports the invalid story.
Resolve the Greptile findings on 74262955:
- Refuse symlinked .stories/, epics/, sprints/, .txn/, .ids/ and
symlinked ledger files, so no read or write leaves .stories/.
- Recovery accepts only regular backup-<n> files in .txn/, validates the
whole journal before restoring, and never writes through a planted
restore.tmp.
- Remove a stale lock only under .lock.break after a re-read, so a live
lock is never deleted; a leftover .lock.break times out (exit 3)
instead of spinning.
- apply enforces one-step-forward status transitions, new stories start
as todo, and Sprint must be unassigned or an existing sprint file.
- Refuse a batch that names the same file twice (e.g. a\b vs a/b).
Symlink tests skip on Windows when symlink creation returns EPERM.
Brings in the three commits main gained since the branch's merge base: the 2.2.3
release, retiring the Everything Claude Code name for ECC, and the ja-JP sponsor
update. 227 files arrive from main; two conflicted.
CHANGELOG.md: the branch had added an Unreleased section and main a released
2.2.3 section, both at the top. Both are kept verbatim with Unreleased above
2.2.3, since none of the branch's entries shipped in that release.
manifests/context-packs/skill-triggers@1.json: both sides had recomputed
registryDigest from the same earlier value to different results, and neither is
the digest of the merged tree. It is recomputed with loadContextRegistry over the
merged tree and only that field changes; triggers and triggersDigest are
untouched on both sides. validate-context-profiles passes.
All twelve CI validators pass. The full suite reports 44 failures in seven files;
43 of them fail by name and count identically on an unmodified upstream/main, and
the remaining one, plugin-hook-bootstrap, fails only inside run-all and passes in
isolation on both trees. The merge introduces no failing test that main does not
already have.
Step 1 skipped binaries while the role promised to verify the whole project,
so a credential in a spreadsheet, a PDF or an embedded database got a clean
PASS and never showed up in the report.
Step 7 now lists every file that doesn't read as text, plus any directory it
can't enter, and the pipeline stops on that list until a person confirms. On
this repo that's a few dozen files, all images and video, which is why they're
warnings and not failures. It doesn't try to extract or scan them: that needs
nested-archive recursion and size bounds, and I'd rather list a file than
half-scan it.
Detection is a first-byte signature check plus a whole-file NUL/control-byte
test, with file(1) only widening the list. If a detector fails, the file is
listed as unverified.
Filenames are untrusted, so Step 7 writes the paths to a manifest beside the
project directory, for a person to open, and keeps them out of its stdout
and its report section. Beside and not inside, so a re-run's text scans
can't read it back. The script prints the count itself as its last line. A
scan problem, a failed write, a walk that dies early or a run that never
reaches that line gives an unknown count, and the gate treats unknown as a
stop, not as zero.
Step 1 no longer skips *.min.js.
Tests in tests/skills/sanitizer-unreadable.test.js. Editing SKILL.md changes
the skill registry digest, so skill-triggers@1.json carries the new value.