Commit Graph
3943 Commits
Author SHA1 Message Date
haelyra df50d56146 Merge PR #3283: serialize session alias mutations 2026-10-10 17:34:44 -04:00
chenhz01 3af000e143 hooks: first operand after -- is still the sed script when no script option was given
Greptile P1: sedScriptParts stopped at -- and discarded the remaining
operands, so 'sed -- <script>' skipped script collection entirely. GNU sed
4.9 (verified): with no -e/--expression anywhere, the first operand after --
is the script and an s///e in it still executes.

The operands after -- are now collected as positionals, so the existing
first-positional-becomes-script rule applies. Operands after the script stay
file names (data). 2 regression tests pin both sides. Suite: 812/812.
2026-10-11 03:55:27 +08:00
chenhz01 95964ee623 hooks: block escaped sed exec fragments conservatively; decompose sed readers
Greptile P1 findings (both verified by running GNU sed against the hook):

1. Escaped replacements evade the check: GNU sed decodes \xHH, \oOOO, \dNNN,
   case operations and unknown escapes like \g before executing an s///e
   replacement or e operand. Scanning the raw text let \x67it decode to git.
   Decoding that surface faithfully is out of scope, so any backslash in an
   exec-bearing fragment now throws UnsupportedSedEscape and the hook blocks
   with an explicit reason — mirroring the existing env -S escape precedent
   instead of pretending to interpret sed. Escaped-newline continuations of an
   e operand are refused by the same check.

2. Continued commands went unchecked: the e operand reader stopped at every
   newline, but GNU sed joins escaped newlines into one operand. Now covered
   by the same conservative refusal.

Also decomposes the sed readers into sub-50-line helpers per the repository
function-size rule (AGENTS.md): readSedDelimited, readSedCommand,
readSedExecute, readSedLineOperand, assertSedLiteral.

5 regression tests pin the escape paths and the benign boundary (a backslash
in a non-executing script stays data). Suite: 810/810.
2026-10-11 03:52:06 +08:00
chenhz01 34a7f41539 hooks: parse sed options through the whole argv — later -e scripts still execute
CodeRabbit security review (Major, CWE-693) correctly identified that GNU sed
collects -e/--expression scripts from any argv position: a script option after
positional operands is still executed, so stopping the option walk at the first
positional let an e-flagged script hide behind it.

Verified against GNU sed 4.9 before fixing:
- 'sed -e s/x/y/ - -e s/y/X/e' runs X (the later -e executes)
- with any script option present, every positional is a file operand

sedScriptParts now walks the full argv, collects scripts from all -e/
--expression positions, and only promotes the first positional to the script
when no script option was given. Unknown options after positionals still fall
back to the opaque default.

4 regression tests pin the reported bypass paths (suite: 805/805).
2026-10-11 03:38:02 +08:00
chenhz01 ffe0e36d46 hooks: model GNU sed execution — script and file operands are data, e/s///e are shell receivers
Ported from the #3051 discussion (test intent, not the old scanner):

- A confidently-parsed sed invocation whose script contains no execution
  construct is a pure text transformation: the script operand and every file
  operand are data. This removes the specific false positives where file names
  or non-executing scripts merely containing 'git ...' were blocked.
- The GNU sed `e` command (bare or with operand) and the `s` command with the
  `e` flag run text through the shell. Their literal fragments (the s
  replacement, the e operand) and any piped/redirected source are scanned as
  real shell source, closing the `printf x | sed 's/x/git commit --no-verify/e'`
  gap reported on 2026-10-09.
- Anything the literal parser cannot confidently read (addresses it cannot
  consume, -f script files, -i suffix ambiguity, unknown options, expansion
  residue) falls back to today's opaque default. No behavior changes for
  unmodeled launchers.
- shell-scan.js is untouched and remains the canonical scanner.

Tests: 14 focused cases ported from the #3051 litmus set; full hook suite
801/801.
2026-10-11 03:15:36 +08:00
Dante 53afd058be Merge remote-tracking branch 'origin/main' into fix/3488-generation-pricing 2026-10-11 01:04:12 +08:00
Dante 27c3ce1c10 fix(hooks): price Claude 5.5 per deduplicated request 2026-10-11 01:04:11 +08:00
Tivonsico 1ad235fdfe fix: report explicitly disabled summaries as skipped 2026-10-11 00:36:27 +08:00
Tivonsico 71bf78796f fix: keep session summaries within the lifecycle deadline 2026-10-11 00:15:12 +08:00
Matt Van Horn 5ec23e8e3d fix: reconcile skill-host-compat with current main
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.
2026-10-10 07:33:04 -07:00
Matt Van Horn e128f4398b Merge branch 'main' into cursor/skill-host-compat-ci-10d0 2026-10-10 07:09:43 -07:00
calm 2cb50cea63 test: isolate Qoder context limits 2026-10-10 21:02:19 +08:00
calm f124f339af test: honor Qoder hook timeout 2026-10-10 20:57:10 +08:00
calm 92728a07bc test: harden Qoder plugin verification 2026-10-10 20:42:11 +08:00
calm 2d9e6977dc feat: add native Qoder plugin support 2026-10-10 20:11:00 +08:00
Seekers2001 d810078424 merge: sync origin/main into docs-test-regression 2026-10-10 17:43:40 +08:00
Seekers2001 83bba4e41f merge: sync origin/main into docs-change-impact 2026-10-10 17:42:23 +08:00
Seekers2001 807ccbfcc0 merge: sync origin/main into docs-context-decisions 2026-10-10 17:40:16 +08:00
Geronimo b40f0f33d7 fix: make concurrency test genuinely overlapping with continuous validity probes
Each writer performs 30 write rounds to widen the race window. Drop the vacuous exitCode liveness assert and the swallowing mid-flight read; instead every wait-loop poll reads and validates the state, so torn reads fail loudly.
2026-10-10 14:29:59 +05:30
Geronimo cd1a253d61 fix: review feedback on installer slice tests
Assert dry-run install writes nothing under projectRoot too (cursor target writes there, not home). Rewrite the concurrent writeInstallState test with genuinely overlapping writers via async spawn plus done-file rendezvous, including a mid-flight read; sanitize rendezvous filenames for Windows.
2026-10-10 14:12:37 +05:30
Geronimo e1546ed45d fix: installer slice from PR #3120 review (Haelyra)
Honor ECC_DRY_RUN=1 in install and repair arg parsing (previously only --dry-run flag worked, so ecc --dry-run install/repair silently performed real writes). Make writeInstallState atomic via writeFileAtomic (O_EXCL staging, fsync, cleanup on failure, atomic rename preserving previous state). Add writeInstallState failure/concurrency/symlink regression tests and disposable-home CLI dry-run regression tests for both global and subcommand forms.
2026-10-10 13:56:00 +05:30
Yann Roberto 310441dcc8 fix(context): honor current-task requests after examples
Track illustrative scopes and retain their connectors so a contrastive
request or rejection for the current task ends the preceding example.
Preserve complete skill aliases, outer discussion guards, quoted task
qualifiers, and indirect citation protection for scored admission.

Invalidate older routing receipts and add resolver/launcher regressions
for request ordering, scope boundaries, aliases, and the reported P1.
2026-10-10 14:15:37 +08:00
Yann Roberto a70b9795d1 fix(context): keep illustrative skill requests on the proposal path
Treat example-framing prefixes as indirect citations before checking
rejections, preserving genuine requests around illustrative instructions.
Keep e.g. together during clause splitting without rewriting e.g.js tokens.

Bump routing policy to v10 to invalidate v9 selections that loaded example
context. Cover resolver, launcher, instruction ordering, and receipt reuse.
2026-10-10 13:09:48 +08:00
Yann Roberto b5a8bf0603 fix(context): preserve skill requests after task preambles
Allow neutral imperative task preambles before an explicit skill request
without admitting final bare mentions, questions, or subject-led reports.
Keep rejection-list scoping intact and recognize must/ought questions.

Bump routing policy to v9 so an empty v8 receipt is reconsidered. Cover
resolver and launcher behavior and document the affected routing helpers.
2026-10-10 12:46:51 +08:00
Yann Roberto 763acfbce0 fix(context): require scoped evidence for automatic skill loading
Keep follow-up questions and reported instructions on the proposal path by
requiring recognized scopes and a direct request prefix for exact admission.
Preserve grouped rejections, personal negation, and genuine directives around
indirect discussion. Invalidate v7 receipts with routing policy v8.

Cover both Greptile reproductions and neighboring resolver/launcher cases,
including unlisted wording, nested reports, and grouped negative lists.
2026-10-10 11:57:53 +08:00
Yann Roberto edeb7b9e9a fix(context): scope compound directives and preserve imperative requests
Limit negation to each independent directive after comma, and, or but.
Accept imperative requests with question punctuation while preserving true
questions, reported speech, and ordered rejections. Bump routing policy to
v7 so prior empty selections are reconsidered.

Add resolver and launcher regressions for the individual cases and their
combinations, including cached selections and actual context injection.
2026-10-10 11:23:53 +08:00
haelyra 4eb71d92a3 Merge pull request #3484 from affaan-m/integration/ecc-043-20261009-verified
fix: guard literal Git include redirects and env split arguments
2026-10-09 21:41:12 -04:00
34a97fa898 fix: reject unsupported env split-string escapes
Validate env -S and --split-string escape syntax before shell scanning or
opaque fallback. Preserve literal single-quoted data and charge each
validation character against the shared input-proportional work budget.

Verified 787 focused cases and 290 real Git/config, harmless native env
and registered-routing controls. Keeps the prior paid-copy protection.

Co-authored-by: Nguyen Thanh Dat <ntdat812.dev@gmail.com>
Co-authored-by: Davemaina1 <dmain7015@gmail.com>
2026-10-09 21:04:20 -04:00
993b5f230f fix: guard literal Git hook redirects and env split arguments
Carry forward bounded literal include/includeIf configuration capabilities
and env -S/--split-string words from PRs #2858 and #2897. Charge array
copy and expansion work before allocation to preserve the shared quota.

Verified 731 hook cases and 60 real Git/config and registered-route
controls. Dynamic remainder and original full source PRs remain held.

Co-authored-by: Nguyen Thanh Dat <ntdat812.dev@gmail.com>
Co-authored-by: Davemaina1 <dmain7015@gmail.com>
2026-10-09 20:41:09 -04:00
montjeffrey 92947a68df Merge upstream/main into feat/gateguard-fewer-round-trips
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.
2026-10-09 20:38:25 -04:00
dependabot[bot] ac5fd9ead9 chore: bump rustls
Bumps the cargo-security group with 1 update in the /ecc2 directory: [rustls](https://github.com/rustls/rustls).


Updates `rustls` from 0.23.37 to 0.23.45
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.37...v/0.23.45)

---
updated-dependencies:
- dependency-name: rustls
  dependency-version: 0.23.45
  dependency-type: indirect
  dependency-group: cargo-security
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-10 00:08:21 +00:00
haelyra 8384d609a0 Merge pull request #3479 from affaan-m/integration/ecc-043-20261009-verified
Integrate reviewed installer, hook, state, and workflow fixes
2026-10-09 20:07:13 -04:00
haelyra aac9b3d34b fix: preserve activity errors, asset bytes and runner summaries
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.
2026-10-09 19:33:37 -04:00
montjeffrey d66e684d4f chore(gitignore): ignore node_modules whether it is a directory or a symlink
`node_modules/` matches directories only, and git does not treat a
symlink as a directory. A node_modules symlink therefore stayed visible
to `git add -A`, which is how one was committed on a PR branch (a link to
/home/claude/affaan-m/ecc/node_modules from the environment that wrote
it). Dropping the trailing slash ignores node_modules at any depth whether
it is a directory, a symlink, or a file; installed dependency directories
stay ignored as before, and no tracked path is affected.
2026-10-09 19:32:26 -04:00
haelyra 984d4f0701 fix: preserve Hookify Windows missing-device identity
Match the Windows missing volume-serial exception while retaining strict reported-device, inode, ownership, permission, link and snapshot checks. Add loader warning/block and race regression controls with isolated platform metadata injection.
2026-10-09 19:10:06 -04:00
montjeffrey beb33e512f docs(context): record the October 9 review follow-up 2026-10-09 19:01:32 -04:00
montjeffrey 0e78d88506 fix(context): publish routing index bytes atomically and repair a partial file
The entries file was written in place with writeExclusive. A build that
stopped mid-write left partial bytes under the digest name, and every
later build refused them ("content changed under its digest"), so the
SessionStart builder could never restore the index.

Write it through the store's atomicJson (temporary file, then rename), so
a digest-named file only ever holds complete bytes, and replace an
existing file whose bytes do not hash to its name instead of trusting it.
2026-10-09 19:01:05 -04:00
montjeffrey 335f329618 test(context): pin recovery from a partial index file and generation-bound content
- A build interrupted while writing the index leaves a partial file under
  its digest name before the pointer exists. The next build rejects those
  bytes ("content changed under its digest") and cannot repair the index.
- A skill edited after the entries are read leaves an index that still
  matches the stored generation, and the next build reports that
  generation stale. This pins the current behavior: the index is bound to
  the generation, not to live sources.
2026-10-09 18:57:52 -04:00
haelyra 5930511997 Merge reviewed #3277 ancestry with the stronger tested config 2026-10-09 18:54:52 -04:00
haelyra 5981dd7092 fix: complete reviewed integration workflows and safety checks 2026-10-09 18:54:39 -04:00
montjeffrey 8dc5fe3082 Merge feat/gateguard-fewer-round-trips into feat/gateguard-question-effectiveness
Bring in the #3248 gate change and its upstream/main merge so this
evaluation branch is based on current main and applies without conflicts.
2026-10-09 18:50:46 -04:00
montjeffrey da8c702435 chore: drop the node_modules symlink committed by mistake
a973e331 tracked node_modules as a symlink to
/home/claude/affaan-m/ecc/node_modules, a path from the environment that
wrote the commit, so it resolves nowhere else. It slipped past .gitignore
because the `node_modules/` pattern matches directories only, and git does
not treat a symlink as one. Dependencies come from a normal install.
2026-10-09 18:46:12 -04:00
haelyra 0cb1476f9d fix: pin and bound Hookify rule reads
Open with supported no-follow and nonblocking flags before relying on file metadata. Validate descriptor, pathname and rule-directory consistency before and after reading at most64KiB plus one overflow byte. Reject observed replacement, mutation, premature EOF and non-regular files, and close descriptors on every outcome.

Add12 race and I/O regressions. Keep the Hermes uppercase protected-file fixture real on case-sensitive filesystems and verify that the installed guard still permits first-time creation.

Validation: full6688 reported tests across347 files,12 race tests, full80% coverage gates, scoped Hookify coverage, installed Hookify and Hermes workflows, packed artifact lifecycle, lint, signatures and IOC.
2026-10-09 17:54:01 -04:00
montjeffrey 0f8a302627 docs(carriers): record the member-call and nested-argument fixes 2026-10-09 17:53:06 -04:00
montjeffrey 5ce12fb9f9 fix(carriers): ignore member calls named require or import, and warn on deeply nested arguments
- Call patterns now skip a require/import preceded by a dot, with or
  without whitespace or optional chaining, so records.require('./label')
  and obj?.import('./x') are method calls rather than module loads.
- Replace the one-level dynamic-argument pattern with a balanced-paren
  scan over the string-free skeleton, so require(path.join(__dirname,
  getName())) and require(a(b(c()))) are reported as dynamic.

Rescanning all 802 repository JavaScript files against the previous head
changes 6 results, each a newly reported dynamic argument nested deeper
than one level (docker eval checks and one test); no specifier or package
result changes. The real registry is unchanged: 35/35 resolved, zero
failures or warnings.
2026-10-09 17:52:55 -04:00
montjeffrey 64ac61e6a3 test(carriers): pin member calls and deeply nested computed arguments
Two first-round review findings were never addressed, and both reproduce
on the current head:

- records.require('./label'), obj?.import('./x') and obj . require('./y')
  are method calls, but the call pattern's word boundary is satisfied by
  the dot, so a self-contained object method fails every projection for a
  file the script never loads.
- The dynamic-argument pattern allows one level of nested parentheses, so
  require(path.join(__dirname, getName())) produces no dynamic warning
  while require(getName()) does.

Control: a genuine require beside the member calls is still a dependency.
2026-10-09 17:50:38 -04:00
montjeffrey 0ca7864ad7 docs(carriers): record the third closure review follow-up 2026-10-09 17:48:50 -04:00
montjeffrey bdb2ceb230 fix(carriers): follow Node's CommonJS candidates, see regex after a block, ignore non-import from
- An extensionless require now tries only the path, .js, .json, and a
  directory's index.js/index.json, as Node does; .cjs and .mjs must be
  named, so require('./helper') no longer passes on helper.cjs alone.
- Treat `}` as a statement boundary, so a regex after a block is read as
  a regex and cannot swallow a later require.
- Decide whether a paren opens a condition from the token before the
  keyword, ignoring whitespace, so `obj.\nif(x) / 2` and
  `obj?. while(x) / 2` stay method calls followed by division.
- Record which slots came from template literals and skip them for
  static `from` and side-effect `import`, which accept only a quoted
  string; a tagged template named `from` is no longer an import.
  A template passed to import() is still a dependency.

Rescanning all 802 repository JavaScript files against dd14493e changes
no result, and the real registry still reports 35/35 resolved with zero
failures or warnings.
2026-10-09 17:48:27 -04:00
montjeffrey 253c051dbf test(carriers): pin CommonJS require candidates, regex after a block, and non-import from
Review on dd14493e found four more ways the closure gate misjudges a
carrier, each reproduced against that head:

- require('./helper') resolves when only helper.cjs or helper.mjs is
  planned, but Node's CommonJS search tries only .js, .json and .node.
- In `if (ok) {} /"/.test(text); require(...)` the regex after the block
  is read as division and its quote swallows the require.
- `obj.\nif(x) / 2` is a method call, but the condition check only saw
  the character immediately before `if`, so the division read as a regex
  and swallowed a later require. Introduced by the previous fix.
- A tagged template named `from` was recorded as an import; with exact
  import resolution it now fails a carrier that never loads that path.
  Introduced by the previous fix.

Controls: an extensionless require still resolves to a .json file, and a
template literal passed to import() is still a dependency.
2026-10-09 17:46:02 -04:00
haelyra 53e606b78e merge: preserve reviewed original and refreshed source ancestry
Keep tested tree 655e700d82 unchanged. Preserve original 3049, 3055, 3115 and 3119 heads alongside their accepted attributed carry-forwards. Preserve parent-approved live 3257 and 3265 heads without replaying generated bindings, old locks, superseded migrations or discarded hook code.
2026-10-09 17:27:57 -04:00