Commit Graph
89 Commits
Author SHA1 Message Date
WinterSold1erandGitHub ee7dded8e9 fix(observer): use _clv2_resolve_homunculus_dir (matches lib export) (#2511)
Commit 2d40baac (PR #2304) renamed `_ecc_*` -> `_clv2_*` but missed this
single call site. The launcher sources `scripts/lib/homunculus-dir.sh` which
only exports `_clv2_resolve_homunculus_dir`, so any user enabling the
observer (`observer.enabled: true`) gets:

  start-observer.sh: line 40: _ecc_resolve_homunculus_dir: command not found

The hook (`observe.sh`) uses the correct name and writes observations, but
the lazy-start path fails silently via nohup, so the symptom is
"observations grow forever, no new instincts". Confirmed on
affaan-m/ECC@40927950c (HEAD of main).

Default `observer.enabled: false` masks the bug for new users. Opt-in
users hit it on first manual `start-observer.sh start` or first lazy-start
after enabling.

Fix: rename the single call to `_clv2_resolve_homunculus_dir` to match
the lib export and every other caller in the skill.
2026-07-20 15:42:14 -04:00
devin-ai-integration[bot]GitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>affaanDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Affaan Mustafa
2d40baacbd fix: resolve open-issue cluster (#2295, #2298, #2303–#2306, #2340) + createdTime fallback bug (#2408)
* fix: resolve issue cluster (#2295,#2298,#2303,#2304,#2305,#2306,#2340) + createdTime fallback bug

- session-manager: fix createdTime birthtime||ctime fallback that never fired
  (a Date is always truthy); use birthtimeMs>0 check via resolveCreatedTime()
- installer: rewrite source-relative rules/skills links for the injected
  ecc/ namespace so installed skills resolve correctly (#2340)
- continuous-learning-v2: drop unused mock import (#2305); standardize bash
  shebangs (#2303); poll for PID file instead of fixed sleep (#2295);
  rename _ecc_* -> _clv2_* (#2304); align promotion confidence docs (#2298);
  de-brittle Scope Decision Guide cross-reference (#2306)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(ci): resync lockfiles with package.json (eslint 10) + migrate yarn.lock to Yarn 4 format

package.json requires eslint@^10.6.0 but the committed locks pinned 9.39.2, so
npm ci aborted and Yarn 4 hardened mode rejected the stale v1-classic yarn.lock
(YN0028). Regenerate package-lock.json and rewrite yarn.lock in Yarn 4 (berry)
format so npm ci and immutable yarn installs both pass.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(ci): require clean probe exit for Windows shell/bash detection; add pyyaml dev dep

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(observer): portable mktemp template on BSD/macOS (#2417); correct false attribution-disabled claim in git-workflow docs (#2426) (#2430)

Co-authored-by: affaan <affaan@itomarkets.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: remove duplicate resolveCreatedTime introduced by merge (no-redeclare)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: restore heading-based Scope Decision Guide ref (line numbers drift) + keep behavioral #2340 install test

---------

Co-authored-by: affaan <affaan@itomarkets.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Affaan Mustafa <me@affaanmustafa.com>
2026-07-03 21:10:45 -07:00
Gaurav DubeyandGitHub 8b6543929e chore(continuous-learning-v2): standardize shell shebangs to env bash (#2401)
* chore(continuous-learning-v2): standardize shell shebangs to env bash

Three scripts under skills/continuous-learning-v2/ used the hardcoded
`#!/bin/bash` shebang while the other four already used the portable
`#!/usr/bin/env bash`:

- hooks/observe.sh (runs on every hook invocation)
- scripts/detect-project.sh
- agents/start-observer.sh

The hardcoded interpreter path fails to execute on systems where bash is
not installed at /bin/bash (NixOS, some Homebrew layouts, FreeBSD).
Standardize all three to `#!/usr/bin/env bash`, matching the repo-wide
majority convention, and add a regression test that asserts shebang
uniformity for every shell script in this skill so the inconsistency
cannot reappear.

Fixes #2303

* test(continuous-learning-v2): harden shebang test runner

Address review feedback on the shebang-consistency regression test:

- firstLine() now splits on /\r?\n/ so a script checked out with CRLF
  line endings does not leave a trailing carriage return that would
  break the shebang comparison on Windows.
- The test() helper now surfaces the full error (stack trace, not just
  the message) on failure and writes pass/fail lines via
  process.stdout/stderr so diagnostics are preserved.

* test(continuous-learning-v2): skip hidden dirs in shebang scan

The recursive shell-script scan now skips hidden directories (e.g. the
observer's runtime `.observer-tmp`). This keeps the shebang-consistency
check deterministic: only committed skill scripts are examined, and an
untracked local artifact left over from an observer run can no longer
cause a false failure.
2026-07-03 20:40:44 -07:00
JunandGitHub 90f82d360b fix(observer): replace hardcoded sleep 2 with PID file poll in start-observer.sh (#2356)
* fix(observer): replace hardcoded sleep 2 with PID file poll in start-observer.sh

Fixes #2295

The previous `sleep 2` after launching the observer loop has two
problems: on slow filesystems or loaded systems 2 seconds may not be
enough, producing a false-negative on the subsequent PID file check; on
healthy systems it adds unnecessary latency.

Replace with a poll loop that exits as soon as the PID file appears:

    for _i in $(seq 1 50); do [ -f "$PID_FILE" ] && break; sleep 0.2; done

50 × 0.2s = 10s max wait (vs the previous fixed 2s), but typical startup
returns within the first iteration. No behavior change in the success
path — only the wait strategy changes.

Tests: `node tests/run-all.js` 2891 passed / 0 failed; `npm run lint`,
`catalog:check`, `command-registry:check` all clean.

* test(observer): add regression guard for sleep-2 → PID-file poll (#2295)

Asserts start-observer.sh never reverts to the fixed `sleep 2` wait and
keeps the 50 × 0.2s `$PID_FILE` poll in place. Sits next to the existing
observer-loop invariant block in tests/hooks/hooks.test.js, matching the
repo's pattern of guarding shell-script invariants via source-content
assertions.

Without this, any future "cleanup" that reintroduces a fixed sleep would
silently regress the slow-filesystem fix from the previous commit.

* fix(observer): loosen poll-regression assertions and document failure-path latency (#2356 review)

Addresses CodeRabbit + Greptile feedback on PR #2356:

- tests/hooks/hooks.test.js: split the over-specific positive assertion
  (which pinned the exact `for _i in $(seq 1 50); … sleep 0.2; done`
  line) into three intent-based assertions — bounded iteration count,
  early-exit on $PID_FILE, sub-second interval. Valid refactors (rename
  loop var, switch to `while`, retune to 100 × 0.1s) no longer false-fail
  while the `sleep 2` regression remains guarded.
- start-observer.sh: extend the inline comment to record the trade-off
  Greptile flagged — a loop that crashes before writing $PID_FILE is now
  detected in ~10s instead of ~2s. Healthy startups still return in
  iteration 1.

Tests: node tests/run-all.js (Node v22.18.0) → 2892 passed / 0 failed.
2026-07-03 20:38:37 -07:00
JunandGitHub 52f7e82a61 docs(continuous-learning-v2): observer.md promotion uses avg confidence, not per-instance (#2411)
SKILL.md and _promote_auto already use avg_conf >= 0.8; observer.md
(EN + zh-CN) was missed and still says per-instance. Same drift as
#2274.

Refs #2298.
2026-07-03 20:37:03 -07:00
jack-finance-ableandGitHub c2bcc4ec2f feat(continuous-learning-v2): make observer model configurable via ECC_OBSERVER_MODEL (#2390)
* feat(continuous-learning-v2): make observer model configurable via ECC_OBSERVER_MODEL

The observer hardcoded `--model haiku`. Parameterize as "${ECC_OBSERVER_MODEL:-haiku}": the haiku default is preserved (no behavior change for existing users), but users can opt into a stronger model — e.g. `ECC_OBSERVER_MODEL=opus` — for higher-quality instinct extraction. Useful on subscription plans where model cost isn't the limiting factor.

* fix(continuous-learning-v2): address review — update wiring test + docs

- Update source-inspection test to assert the ${ECC_OBSERVER_MODEL:-haiku} defaulting behavior (was matching the literal `claude --model haiku`, which this PR changed). All 31 tests pass.
- Add guidance to raise ECC_OBSERVER_TIMEOUT_SECONDS for slower models (e.g. opus) so the 120s watchdog doesn't kill analysis mid-run.
- Fix now-stale 'Haiku session' comment -> 'observer session' (model is configurable).
2026-06-29 18:43:42 -07:00
Gaurav DubeyandGitHub f720885cea fix(clv2): archive observations only after successful analysis in observer-loop (#2386)
analyze_observations moved observations.jsonl into observations.archive/
unconditionally, even when the Claude analysis failed (timeout, non-zero
exit, rate limit). Because the analyzer only reads the live file, a failed
batch was archived and never re-analyzed, silently dropping the instincts
it would have produced.

Return early on a non-zero analysis exit so the archive mv runs only on
success, retaining observations for the next cycle to retry. Resolve the
script's own directory from ${BASH_SOURCE[0]} (SCRIPT_DIR) so sibling
scripts (session-guardian.sh) and relative helpers resolve correctly under
both execution and sourcing, and add a source-guard so observer-loop.sh can
be sourced without starting the loop. Add a regression test covering both
the failure (retain) and success (archive) paths.

Fixes #2370
2026-06-29 18:43:37 -07:00
Gaurav DubeyandGitHub a36148fff9 test(clv2): add coverage for instinct-cli prune, projects ops, promote dry-run, normalize-url (#2374)
* test(clv2): cover instinct-cli prune, projects ops, promote dry-run, normalize-url

Add pytest coverage for previously-untested functions in
skills/continuous-learning-v2/scripts/instinct-cli.py:

- _normalize_remote_url: scp/https/file forms, credential + .git
  stripping, network lowercasing, case-preserving local paths, idempotence
- _promote_specific dry-run: returns 0 and writes no global file
- projects delete/gc/merge: invalid-id, not-found, dry-run, and force
  paths over registry + storage, asserting destructive ops are gated
- cmd_prune: dry-run keeps files; non-dry-run deletes only expired; quiet

Test-only change; no production code modified.

Fixes #2302

* test(clv2): assert dry-run storage no-op and quiet-mode stderr silence

Address CodeRabbit review on #2374:

- projects gc/merge dry-run tests now also assert on-disk storage is
  untouched (empty1 project dir survives; nothing copied into dest
  personal), closing the gap where a storage-mutating dry-run regression
  would still pass.
- cmd_prune quiet test now asserts stderr is empty too, not just stdout.

* test(clv2): cover merge missing-destination and prune empty-pending branches
2026-06-29 18:43:32 -07:00
Gaurav DubeyandGitHub a6d12ec21e fix(clv2): surface SIGALRM timeout drops in observe.sh (#2373)
* fix(clv2): surface SIGALRM timeout drops in observe.sh

The inline-Python observation writers in observe.sh arm a signal.SIGALRM
alarm (8s) so they self-terminate before the async hook's 10s timeout can
orphan them (#2278). The handler _ecc_bail called sys.exit(0) with no
logging, so when the alarm fired the in-flight observation was silently
dropped: nothing was logged, no partial write occurred, and the shell saw
a clean exit. There was no way to detect or count how many observations
were being lost.

Add a single stderr visibility line to both _ecc_bail handlers (the
parse-error fallback path and the main observation-writing path) before
sys.exit(0), using the repo's "[observe]" log prefix. Exit code stays 0:
in a Claude Code hook a non-zero exit signals a block, so changing it
would turn an internal timeout into a user-facing tool block. The warning
goes to stderr (not stdout) because both blocks redirect stdout into the
observations file.

Add tests/hooks/observe-signal-timeout.test.js: a static regression guard
that every _ecc_bail handler logs to stderr before exiting and keeps exit
0, plus a behavioral check that runs the real handler text extracted from
observe.sh and confirms a fired alarm exits 0 and emits the [observe]
warning on stderr only.

Fixes #2300

* test(clv2): exercise both _ecc_bail handlers end-to-end

The behavioral SIGALRM-fire test ran only handlers[0] (the parse-error
fallback path); the main observation-write path (handlers[1]) was covered
only by the static regex guard. The write path is the higher-value one to
verify end-to-end since it carries valid, parseable data that would succeed
given more time, so a silent drop there is the worst case.

Loop the behavioral check over every extracted handler so a regression that
silenced the second handler's stderr write is caught at runtime, not just by
the static guard.

* test(clv2): select timeout handlers by marker, not array index

The behavioral check looped over all extracted _ecc_bail handlers by index.
If an unrelated _ecc_bail were ever added to observe.sh, the loop would
either test the wrong block or be diluted. Filter the handlers to those
carrying the "[observe] SIGALRM timeout" marker so the live SIGALRM check
stays pinned to the two #2300 timeout handlers regardless of array order or
future additions.

* test(clv2): fail fast when python is missing in SIGALRM check

The behavioral test returned early when no python interpreter was found,
which the test harness records as a PASS — so the SIGALRM contract could go
entirely unverified yet still look green. Throw instead, matching the
existing insaits-security-monitor convention of failing when a required
Python runtime is absent, and drop the in-test console.log.
2026-06-29 18:43:28 -07:00
Gaurav DubeyandGitHub a89b32c2b5 fix(clv2): serialize observer signal-counter to stop dropped increments (#2372)
observe.sh bumps the SIGUSR1 throttle counter in
${PROJECT_DIR}/.observer-signal-counter with an unlocked read-modify-write.
The hook runs on every tool call, so concurrent invocations read the same
value, both increment, and lose a write, signaling the observer at
unpredictable intervals and defeating the #521 throttle.

Serialize the read-modify-write under a lock, and only ever bump the counter
while that lock is held:

- Prefer flock with a bounded -w wait (the OS auto-releases it when the fd
  closes or the process dies, so there is no stale lock and no lost increment);
  on a timeout the tick is skipped rather than bumped unlocked.
- Fall back to an atomic mkdir lock on platforms without flock, with a bounded
  spin. An EXIT trap cleans up on normal completion; INT/TERM traps release the
  lock and exit, so a signal cannot drop the lock and then continue the
  read-modify-write without ownership. If the lock cannot be acquired in the
  budget the tick is skipped rather than raced. No hand-rolled PID stale-reclaim
  (which is racy and can delete a live re-acquirer's lock).
- Guard the counter read against a corrupt (non-integer) file that would abort
  the hook under set -e.

Add tests/hooks/observe-signal-counter-race.test.js: 20 concurrent observe.sh
invocations must not lose increments (exact under flock; at most one dropped on
the best-effort mkdir fallback), the runner rejects on any hook execution
failure or hang, plus content guards for the lock and the corrupt-counter
handling.

Fixes #2296
2026-06-29 18:43:23 -07:00
f12b106c3c fix(clv2): align Python _update_registry schema with shell counterpart (#2369)
* fix(clv2): align Python _update_registry schema with shell counterpart

The Python `_update_registry` in instinct-cli.py wrote registry entries
without the `id` and `created_at` fields, while the shell counterpart in
detect-project.sh writes both. A projects.json entry could therefore have a
different shape depending on which path (Python CLI or shell hook) last
touched it.

Emit the same field set and order as the shell version: id, name, root,
remote, created_at (preserved from any existing entry), last_seen. Add
regression tests asserting field parity and created_at preservation.

Fixes #2299

* fix(clv2): guard _update_registry against a non-dict registry entry

A malformed projects.json (a non-dict value for the current project id, e.g.
null) would make existing.get("created_at", ...) raise and crash the update,
losing the old code's ability to self-heal a corrupt per-entry value. Normalize
existing to {} when it is not a dict so the entry is healed by the rewrite. Add
a regression test for the malformed-entry path.

* test(clv2): assert the first-write created_at == last_seen contract

The new _update_registry tests only checked both timestamps were truthy. On the
initial write both derive from the same `now`, so created_at must equal
last_seen; assert that explicitly so a later refactor that breaks the contract
is caught. Split the compound assertions into single-expression checks.

* fix(clv2): heal a non-dict top-level registry in _update_registry

A projects.json that is valid JSON but not a mapping (e.g. `[]` or a
string) previously crashed _update_registry on registry.get(), before
the per-entry guard could run, so the corrupt file could not be healed.
Guard the top-level shape right after the load and fall back to {} so the
rewrite repairs the file — matching the per-entry healing already in place.

Resolves the remaining CodeRabbit finding on #2299.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 18:43:19 -07:00
85dfb07576 Fix for docs: Scope Decision Guide table duplicated in SKILL.md and observer.md with minor drift (#2366)
#2306

Co-authored-by: angadsingh7666 <angdsingh7666@gmail.com>
2026-06-29 15:50:49 -07:00
Affaan MustafaandGitHub 2bc924faf2 fix(clv2): harden registry writes and project deletion (#2294, #2297) (#2323)
Two security-priority fixes in continuous-learning-v2/scripts/instinct-cli.py:

- #2294: _write_registry wrote projects.json without the advisory lock that
  _update_registry holds, so concurrent 'projects delete/gc/merge' could race an
  observe-time update and corrupt the registry. Extract the lock into a shared
  _registry_lock() context manager and use it in both writers.

- #2297: _remove_project_storage called shutil.rmtree on PROJECTS_DIR/project_id
  with no containment check. Add defense-in-depth: resolve the path and refuse to
  delete anything that is not strictly inside PROJECTS_DIR (or is the root
  itself), so a relaxed validator or future caller can never cause an
  arbitrary-directory delete.

Adds 5 pytest regression tests (atomic write under lock, contained delete,
missing-dir no-op, traversal refused, root refused). Node integration suite
(tests/scripts/instinct-cli-projects.test.js) green 9/9.
2026-06-25 16:47:35 -07:00
Gaurav DubeyandGitHub e3f467989a fix(clv2): escape $HOME before pgrep -f in migrate-homunculus.sh (#2339)
* fix(clv2): escape $HOME before pgrep -f in migrate-homunculus.sh

pgrep -f treats its argument as an extended regular expression, but the
running-observer guard interpolated $HOME unescaped. Paths containing regex
metacharacters (e.g. /home/user.name, /home/c++dev, /home/user (work)) made the
match over-broad or invalid, causing either a false negative (live observer
missed, migration proceeds and risks registry corruption) or a false positive
(migration blocked unnecessarily).

Escape the ERE metacharacters in $HOME via sed before building the pattern so
the home prefix is matched literally while the trailing .*observer-loop\.sh
regex is preserved. Portable across BSD and GNU sed.

Fixes #2301

* test(clv2): add regression test for migrate-homunculus.sh $HOME escaping

Guards the #2301 fix: extracts the script's sed escaping command and asserts
the resulting pgrep -f pattern matches the literal home path while no longer
over-matching a regex-expanded decoy (HOME=/home/user.name must not match
/home/userXname). Also pins that the guard uses escaped_home rather than $HOME
directly. Follows the existing clv2 shell-test convention in
tests/hooks/observe-entrypoint-allowlist.test.js.

Refs #2301

* test(clv2): skip migrate-homunculus escaping test on Windows

The test relies on POSIX bash/sed/grep -E semantics, which differ on the
Windows CI runners. Guard with the same process.platform === 'win32' early
exit used by tests/hooks/observe-subdirectory-detection.test.js so the
bash-dependent assertions only run on POSIX platforms.

Refs #2301
2026-06-25 16:47:32 -07:00
Affaan Mustafa 51184b692e fix(continuous-learning): eliminate _SECRET_RE catastrophic backtracking + orphaned-CPU hang (#2278)
The observe hook's secret-scrub regex used a generic ([A-Za-z]+\s+)? group
that overlapped the separator and value classes, causing exponential
backtracking on identifier-dense tool I/O — an orphaned python child then
pegged a core at ~100% CPU for days because the async hook timed out without
killing it.

- Rewrite _SECRET_RE as a linear matcher: bounded separator {1,8}, a fixed
  set of auth schemes (bearer|basic|token|bot) instead of [A-Za-z]+, and a
  bounded value {8,256}. Pathological input drops from hang to <1ms; real
  secrets still redact (verified incl. 'Bearer <token>').
- Add a signal.alarm(8) self-timeout to both scrub blocks so any runaway
  child self-terminates before the 10s async-hook timeout can orphan it.
2026-06-18 16:34:11 -04:00
BERORINPO db7f2a6fd5 fix(skills): move top-level origin frontmatter key under metadata
The official Agent Skills spec (agentskills.io/specification) whitelists exactly
6 top-level frontmatter keys (name/description/license/compatibility/metadata/
allowed-tools). A top-level `origin` key fails the official validator
(anthropics/skills quick_validate.py ALLOWED_PROPERTIES; skills-ref validate).

This moves `origin: X` -> `metadata.origin: X` across the canonical skills/
tree, preserving each value verbatim. Frontmatter-only, minimal diff.

- 251 SKILL.md updated (242 new metadata block, 9 appended to existing metadata)
- origin values preserved verbatim (verified 251/251)
- YAML validated on all changed files
- scoped to canonical skills/ only (docs/<lang> translations + tool mirrors
  .cursor/.kiro/.agents left untouched; presumably regenerated from canonical)

Addresses #2233
2026-06-11 21:12:21 +09:00
ECC Test 3c5bcc2b66 security: harden advisory intake and dependency coverage 2026-06-09 20:46:14 -04:00
e4dfc1679b fix: surface legacy data warning in instinct-cli status (#2127)
* fix: surface legacy data warning in instinct-cli status (#2036)

When the data directory moved from ~/.claude/homunculus/ to the
XDG-compliant ~/.local/share/ecc-homunculus/, legacy installs with data
still in the old path saw "No instincts found" with no explanation.

Add _warn_legacy_data() to cmd_status so users get a clear, actionable
warning pointing them to the migration script or the CLV2_HOMUNCULUS_DIR
override. Wrap the directory scan in try/except to handle permission
errors gracefully.

Closes #2036

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: address review feedback — drop unused f-strings, resolve absolute migrate path

Remove extraneous f-prefix from strings without interpolation (ruff F541).
Resolve migrate-homunculus.sh path relative to instinct-cli.py instead of
hard-coding a repo-relative path that only works from the repo root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: quote migrate script path to handle spaces

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: kky <lingmu141592@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 13:26:22 +08:00
a3d8d8ab92 fix(observer): auto-scale max_turns by analysis batch size (#2062)
* fix(observer): auto-scale max_turns by analysis batch size (#2035)

The hardcoded default of MAX_TURNS=20 is insufficient when
MAX_ANALYSIS_LINES=500 (also the default). Claude exhausts its turn
budget before it can write all discovered instinct files, producing:

  Error: Reached max turns (20)

Fix: when ECC_OBSERVER_MAX_TURNS is not explicitly set, compute
max_turns proportionally to the actual analysis batch size:
  max_turns = clamp(analysis_count / 10, 20, 100)

This gives:
  - 20–199 lines → 20 turns  (existing floor, unchanged)
  - 500 lines    → 50 turns  (resolves the reported failure)
  - 1000 lines   → 100 turns (cap)

Explicitly setting ECC_OBSERVER_MAX_TURNS still overrides the
auto-scaled value, preserving the existing escape hatch.

* test(observer): update max_turns test for auto-scaling; document validation

The max-turns budget test in tests/hooks/hooks.test.js still asserted the removed literal max_turns="${ECC_OBSERVER_MAX_TURNS:-20}", which would fail against the new auto-scaling logic. Assert the auto-scale formula and the 20/100 clamp bounds instead.

Also add the explanatory comment CodeRabbit requested above the max_turns sanitization block, clarifying it guards the explicit ECC_OBSERVER_MAX_TURNS override path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:25:29 +08:00
Gaurav DubeyandGitHub 30ef079e7e fix(continuous-learning-v2): accept claude-vscode as valid entrypoint (#2134)
The observe.sh Layer 1 entrypoint guard short-circuits with exit 0 when
CLAUDE_CODE_ENTRYPOINT is not in {cli, sdk-ts, claude-desktop}. Claude
Code's VS Code extension sets CLAUDE_CODE_ENTRYPOINT=claude-vscode, so
VS Code users see no observations recorded — observations.jsonl never
gets created and the instinct pipeline stays empty.

Add claude-vscode to the allowlist, mirroring the precedent in #1522
which added claude-desktop the same way.

Add a regression test that spawns observe.sh under bash -x for each
allowed entrypoint (cli, sdk-ts, claude-desktop, claude-vscode) and
each denied entrypoint (unknown-host, claude-cody, mcp), asserting
that allowed entrypoints reach Layer 2's ECC_HOOK_PROFILE check while
denied entrypoints stop at Layer 1.

Fixes #2102
2026-06-07 13:01:24 +08:00
Affaan MustafaandAffaan Mustafa bc519e5b8e fix(learning): add project registry maintenance 2026-05-19 12:51:18 -04:00
Affaan MustafaandAffaan Mustafa 6d130cfcd5 fix: reduce observer hook scanner signatures 2026-05-16 15:26:25 -04:00
Affaan MustafaandGitHub d4728a0d80 fix: fall back to ASCII instinct status bars
Fixes #1855
2026-05-13 02:59:58 -04:00
Affaan MustafaandAffaan Mustafa 9b385c9e30 fix: salvage stale PR plugin install fixes 2026-05-11 18:48:23 -04:00
Affaan MustafaandGitHub c45aeee57f fix: salvage remaining stale queue fixes (#1754) 2026-05-11 16:41:08 -04:00
Affaan MustafaandGitHub 12e1bc424d fix: port continuous-learning observer fixes
Ports continuous-learning observer signal, storage, remote normalization, and v1 deprecation fixes onto current main.
2026-05-11 03:35:42 -04:00
Affaan Mustafa fd95cf6b29 fix: retry observer wait after signal 2026-04-28 22:11:47 -04:00
Affaan MustafaandGitHub 4e66b2882d docs: fix plugin quick start for continuous learning v2 (#1546) 2026-04-21 18:41:36 -04:00
suusuu0927andGitHub e63241c699 fix(observe): skip Windows AppInstallerPythonRedirector.exe in resolve_python_cmd (#1511)
On Windows 10/11 without Python installed from the Microsoft Store, the
"App Execution Alias" stubs at %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe
and python3.exe are symlinks to AppInstallerPythonRedirector.exe. These
stubs neither launch Python nor honor `-c`; calls print a bare "Python "
line and exit, silently breaking every JSON-parsing step in observe.sh.

Net effect: observations.jsonl is never written, CLV2 appears installed
correctly, and the only residual artifact is `.last-purge`.

This commit:
  1. Adds `_is_windows_app_installer_stub` helper that detects the stub
     via `command -v` output and optional `readlink -f` resolution.
  2. Teaches `resolve_python_cmd` to skip stub candidates and fall
     through to the next real interpreter (typically C:\...\Python3xx\python.exe).
  3. Exports the stub-aware CLV2_PYTHON_CMD before sourcing
     detect-project.sh, which already honors an already-set value,
     so the shared helper does not re-resolve and re-select the stub.

POSIX-compatible. No behavior change on macOS / Linux / WSL where no
such stub exists.

Refs: observations.jsonl empty on Windows Claude Desktop users.
2026-04-21 18:39:06 -04:00
Zhao-Ming ZhongandGitHub 81bde5c3cd fix(continuous-learning-v2): accept claude-desktop as valid entrypoint (#1522) 2026-04-21 18:37:23 -04:00
suusuu0927 797692d70f fix: make detect-project.sh locale-independent and handle Windows backslash paths
Two bugs in skills/continuous-learning-v2/scripts/detect-project.sh that
silently split the same project into multiple project_id records:

1. Locale-dependent SHA-256 input (HIGH)
   The project_id hash was computed with
     printf '%s' "$hash_input" | python -c 'sys.stdin.buffer.read()'
   which ships shell-locale-encoded bytes to Python. On a system with a
   non-UTF-8 LC_ALL (e.g. ja_JP.CP932 / CP1252) the same project root
   produced a different 12-char hash than the UTF-8 locale would produce,
   so observations/instincts were silently written under a separate
   project directory. Fixed by passing the value via an env var and
   encoding as UTF-8 inside Python, making the hash locale-independent.

2. basename cannot split Windows backslash paths (MEDIUM)
   basename "C:\Users\...\ECC作成" returns the whole string on POSIX
   bash, so project_name was garbled whenever CLAUDE_PROJECT_DIR was
   passed as a native Windows path. Normalize backslashes to forward
   slashes before calling basename.

Both the primary project_id hash and the legacy-compat fallback hash
are updated to use the env-var / UTF-8 approach.

Verified: id is stable across en_US.UTF-8, ja_JP.UTF-8, ja_JP.CP932, C,
and POSIX locales; Windows-path input yields project_name=ECC作成;
ASCII-only paths regress-free.
2026-04-21 18:46:39 +09:00
Affaan Mustafa df96abe74c fix: harden windows observer prompt handling 2026-04-06 14:05:38 -07:00
Affaan Mustafa 908116d736 fix: raise observer turn budget 2026-04-05 14:23:06 -07:00
Affaan Mustafa 16e9b17ad7 fix: clean up observer sessions on lifecycle end 2026-04-02 18:02:29 -07:00
Affaan Mustafa 70be8f9f44 fix: add POSIX fallback for observer lazy-start 2026-04-02 17:07:32 -07:00
Affaan Mustafa 3f6a14acde fix(clv2): resolve cwd to git root before project detection 2026-03-30 04:46:31 -04:00
Affaan Mustafa be76918850 fix(clv2): honor CLV2_CONFIG in start-observer 2026-03-29 00:21:55 -04:00
Lidang-Jiang 7a4cb8c570 fix(observer): clean up prompt_file early and fix test for analysis_relpath
- Remove prompt_file immediately after shell expansion into -p arg,
  avoiding stale temp files during long analysis windows (greptile feedback)
- Update test assertion to check analysis_relpath instead of analysis_file,
  matching the cross-platform relative path change from earlier commits

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
2026-03-28 23:32:44 +08:00
Lidang-JiangandClaude Opus 4.6 451732164f fix(observer): clean up temp files on cd failure early return
The cd "$PROJECT_DIR" failure path returned without removing prompt_file
and analysis_file, leaving stale temp files in .observer-tmp/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
2026-03-28 11:38:10 +08:00
Lidang-JiangandClaude Opus 4.6 194bc0000b fix(observer): guard cd failure with early return and log message
Address reviewer feedback: under set +e, a failing cd would silently
leave CWD unchanged, causing the relative analysis path to break.
Add || return with a diagnostic log entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
2026-03-28 10:45:41 +08:00
Lidang-JiangandClaude Opus 4.6 31af1adcc8 fix(observer): anchor CWD to PROJECT_DIR before Claude invocation
Reviewers correctly identified that the relative analysis_relpath
(.observer-tmp/<file>) only resolves when CWD equals PROJECT_DIR.
Without an explicit cd, non-Windows users launching the observer from
a different directory would fail to read the analysis file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
2026-03-28 10:34:40 +08:00
Lidang-JiangandClaude Opus 4.6 c80631fc1d fix(observer): improve Windows compatibility for temp files and Haiku prompt
Address remaining issues from #842 after PR #903 moved temp files to
PROJECT_DIR/.observer-tmp:

Bug A (path resolution): Use relative paths (.observer-tmp/filename)
in the prompt instead of absolute paths from mktemp. On Windows
Git Bash/MSYS2, absolute paths use MSYS-style prefixes (/c/Users/...)
that the spawned Claude subprocess may fail to resolve.

Bug B (asks for permission): Add explicit IMPORTANT instruction block
at the prompt start telling the Haiku agent it is in non-interactive
--print mode and must use the Write tool directly without asking for
confirmation.

Additional improvements:
- Pass prompt via -p flag instead of stdin redirect for Windows compat
- Add .observer-tmp/ to .gitignore to prevent accidental commits

Fixes #842

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
2026-03-28 09:54:35 +08:00
Affaan Mustafa 1d0aa5ac2a fix: fold session manager blockers into one candidate 2026-03-24 23:08:27 -04:00
0e733753e0 feat: pending instinct TTL pruning and /prune command (#725)
* feat: add pending instinct TTL pruning and /prune command

Pending instincts generated by the observer accumulate indefinitely
with no cleanup mechanism. This adds lifecycle management:

- `instinct-cli.py prune` — delete pending instincts older than 30 days
  (configurable via --max-age). Supports --dry-run and --quiet flags.
- Enhanced `status` command — shows pending count, warns at 5+,
  highlights instincts expiring within 7 days.
- `observer-loop.sh` — runs prune before each analysis cycle.
- `/prune` slash command — user-facing command for manual pruning.

Design rationale: council consensus (4/4) rejected auto-promote in
favor of TTL-based garbage collection. Frequency of observation does
not establish correctness. Unreviewed pending instincts auto-delete
after 30 days; if the pattern is real, the observer will regenerate it.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

* fix: remove duplicate functions, broaden extension filter, fix prune output

- Remove duplicate _collect_pending_dirs and _parse_created_date defs
- Use ALLOWED_INSTINCT_EXTENSIONS (.md/.yaml/.yml) instead of .md-only
- Track actually-deleted items separately from expired for accurate output
- Update README.md and AGENTS.md command counts: 59 → 60

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

* fix: address Copilot and CodeRabbit review findings

- Use is_dir() instead of exists() for pending path checks
- Change > to >= for --max-age boundary (--max-age 0 now prunes all)
- Use CLV2_PYTHON_CMD env var in observer-loop.sh prune call
- Remove unused source_dupes variable
- Remove extraneous f-string prefix on static string

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

* fix: update AGENTS.md project structure command count 59 → 60

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: address cubic and coderabbit review findings

- Fix status early return skipping pending instinct warnings (cubic #1)
- Exclude already-expired items from expiring-soon filter (cubic #2)
- Warn on unparseable pending instinct age instead of silent skip (cubic #4)
- Log prune failures to observer.log instead of silencing (cubic #5)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: YAML single-quote unescaping, f-string cleanup, add /prune to README

- Fix single-quoted YAML unescaping: use '' doubling (YAML spec) not
  backslash escaping which only applies to double-quoted strings (greptile P1)
- Remove extraneous f-string prefix on static string (coderabbit)
- Add /prune to README command catalog and file tree (cubic)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
2026-03-22 15:40:58 -07:00
Affaan Mustafa 6b2de1baff security: remove supply chain risks, external promotions, and unauthorized credits
- Remove zenith.chat references and @DRodriguezFX shoutout from README
- Remove Inspiration Credits section (already in CHANGELOG.md)
- Remove awesome-agent-skills reference from Links
- Remove Plankton H3 section by @alxfazio (skill stays in skills/)
- Remove brand names (InsAIts, VideoDB, Evos) from v1.9.0 notes
- Remove @ericcai0814 individual credit from README (kept in CHANGELOG)
- Add Security Guide to Links section
- Replace curl-pipe-to-bash in autonomous-loops with review warning
- Replace git clone in plankton-code-quality with review warning
- Replace pip install git+ in agent-eval with review warning
- Replace npm install -g in dmux-workflows with review warning
- Add commercial API notice to nutrient-document-processing
- Remove VideoDB maintainer credit from videodb skill
- Replace skill-creator.app link with ECC-Tools GitHub App reference
2026-03-21 18:10:05 -07:00
Affaan MustafaandGitHub a6bd90713d Merge pull request #664 from ymdvsymd/fix/observer-sandbox-access-661
fix(clv2): add --allowedTools to observer Haiku invocation (#661)
2026-03-20 00:16:42 -07:00
to.watanabeandClaude Opus 4.6 04f8675624 fix(clv2): use -e instead of -d for .git check in detect-project.sh
In git worktrees, .git is a file (not a directory) containing a gitdir
pointer. The -d test fails for worktree checkouts, causing project
detection to fall through to the "global" fallback. Changing to -e
(exists) handles both regular repos and worktrees correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:02:10 +09:00
to.watanabeandClaude Opus 4.6 f37c92cfe2 fix(clv2): add --allowedTools to observer Haiku invocation (#661)
The observer's Haiku subprocess cannot access files outside the project
sandbox (/tmp/ for observations, ~/.claude/homunculus/ for instincts).
Adding --allowedTools "Read,Write" grants the necessary file access
while keeping the subprocess constrained by --max-turns and timeout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:00:17 +09:00
Chris YauandGitHub a9edf54d2f fix(observe): allow sdk-ts entrypoint in observation hook (#614)
Clean surgical fix allowing sdk-ts entrypoint in observe hook for Agent SDK sessions. Has APPROVED review.
2026-03-19 20:49:15 -07:00
Affaan MustafaandGitHub f9e8287346 fix: observer memory explosion with throttling, re-entrancy guard, and tail sampling (#536)
Three fixes for the positive feedback loop causing runaway memory usage:

1. SIGUSR1 throttling in observe.sh: Signal observer only every 20
   observations (configurable via ECC_OBSERVER_SIGNAL_EVERY_N) instead
   of on every tool call. Uses a counter file to track invocations.

2. Re-entrancy guard in observer-loop.sh on_usr1(): ANALYZING flag
   prevents parallel Claude analysis processes from spawning when
   signals arrive while analysis is already running.

3. Cooldown + tail-based sampling in observer-loop.sh:
   - 60s cooldown between analyses (ECC_OBSERVER_ANALYSIS_COOLDOWN)
   - Only last 500 lines sent to LLM (ECC_OBSERVER_MAX_ANALYSIS_LINES)
     instead of the entire observations file

Closes #521
2026-03-16 13:47:25 -07:00