Skip to content

Weekly upstream sync 2026-05-03 (8 commits) - #2

Open
loganbronstein wants to merge 15 commits into
mainfrom
upstream-week-2026-05-03
Open

loganbronstein wants to merge 15 commits into
mainfrom
upstream-week-2026-05-03

Conversation

@loganbronstein

Copy link
Copy Markdown
Owner

HEADS UP: this merge had conflicts. The diff includes conflict markers (<<<<<<<, =======, >>>>>>>). Logan, decide which side wins.

Weekly upstream sync (2026-05-03)

Upstream grandamenium/cortextos added 8 commit(s) since the last run.
Each one is summarized in plain English below.


What's new

  1. New feature in bus: hooks framework — Day-1 stub + Day-2 per-handler wiring + telemetry (feat(bus): hooks framework — Day-1 stub + Day-2 per-handler wiring + telemetry grandamenium/cortextos#272)

  2. Bug fix in telegram: validate BOT_TOKEN and CHAT_ID against Telegram API before enable + setup writes .env (fix(telegram): validate BOT_TOKEN and CHAT_ID at add-agent and setup time grandamenium/cortextos#235)

  3. Bug fix in daemon: extend gap detection to cron-expression crons (fix(daemon): gap detection skips cron-expression crons — only interval-based crons monitored grandamenium/cortextos#169) (fix(daemon): extend gap detection to cron-expression crons (#169) grandamenium/cortextos#184)

  4. Bug fix in telegram: switch to HTML parse mode — eliminates silent content drops (fix(telegram): switch to HTML parse mode — eliminates silent content drops grandamenium/cortextos#181)

  5. Bug fix in daemon: use CronCreate directly on boot to skip /loop cloud-prompt (fix(daemon): use CronCreate directly on boot to skip /loop cloud-prompt grandamenium/cortextos#210)

  6. Bug fix in bus: hard-restart now sends IPC restart-agent to terminate the session (fix(bus): hard-restart now sends IPC restart-agent to terminate the session grandamenium/cortextos#217)

  7. Bug fix in daemon: guard worker PTY null-write + add crash visibility (fix(daemon): guard worker PTY null-write + add crash visibility grandamenium/cortextos#223)

  8. Bug fix in test: use relative timestamps in channels route test (fix(test): use relative timestamps in channels route test grandamenium/cortextos#226)


How to merge

  • Reply to this PR with merge all to take everything.
  • Reply with merge 1 3 5 (numbers from the list above) to cherry-pick those.
  • Reply with skip to dismiss this whole PR.
  • If there's a merge conflict, you'll see it on the Files tab. Logan tells boss what to do.

This PR was opened automatically by boss every Sunday at 6:30pm Chicago time.

loganbronstein and others added 15 commits April 22, 2026 04:12
Auto-regenerated by `cortextos ecosystem`. Switches from path.join helpers
to inline absolute paths plus process.env overrides for PM2 runtime
flexibility. max_restarts raised so crash loops have more headroom
before PM2 gives up. Daily auto-commit snapshot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a silent auto-reset tier that fires BEFORE the graceful handoff tier,
so agents get snapshotted and force-restarted at e.g. 55 percent without
user-facing Telegram noise.

- New ctx_autoreset_threshold in AgentConfig (0 or absent = disabled)
- FastChecker.checkContextStatus grows a Tier 0 check that fires once per
  session, takes a synchronous best-effort snapshot, resets
  context_status.json pre-emptively, and routes through forceContextRestart
  so the existing circuit breaker + hardRestart + sessionRefresh path runs
- scripts/snapshot-agent.sh factored from pre-compact-snapshot.sh with a
  --silent flag; daemon always invokes silent, ops can invoke with --notify
- cortextos bus auto-compact-agent <name> manual ops hatch (silent default)
- Idempotency: .restart-planned present and <2min old blocks Tier 0;
  stale markers (>2min) are ignored so a leaked marker cannot permanently
  disable the tier
- Unit tests for Tier 0 selection (disabled/fired/deadline precedence,
  malformed pct, marker idempotency) and integration tests for
  autoCompactAgent (silent vs notify, snapshot failures, missing script)
- AGENTS.md Context Discipline section documents the four tiers and the
  manual hatch

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three fixes from the adversarial review pass:

1. NON-BLOCKING SNAPSHOT: runAutoresetSnapshot now uses spawn with
   detached+unref instead of execFileSync. The 1s FastChecker poll loop
   is no longer held for up to 10s while the snapshot runs, which was
   delaying Telegram delivery, inbox checks, and typing-indicator updates.

2. BOOT-WINDOW GUARD: Tier 0 refuses to fire within 60s of session start.
   Without this, an agent that boots at or above the autoreset threshold
   (bloated CLAUDE.md, pre-loaded handoff doc, heavy bootstrap) would
   enter a restart loop — each fresh session crosses the threshold within
   the first tick and trips Tier 0 again. We do NOT latch the fired-at
   flag during the boot window so later polls can reconsider.

3. CLOCK-SKEW-SAFE MARKER STALENESS: the .restart-planned marker age
   check now treats negative ages (mtime in the future, from system clock
   jumps) as stale instead of fresh. Previously a backward clock jump
   could silently disable Tier 0 by making a stale marker look fresh.

Defense-in-depth: forceContextRestart now also zeros ctxAutoresetFiredAt
alongside the other per-session context flags, so a future soft-refresh
path that reuses the same FastChecker instance would not latch Tier 0
off forever.

New tests cover boot-window gate (0s/59s/60s/10min boundary), clock-skew
marker staleness (negative age, 2min boundary), and legacy no-session_id
fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four functional issues from the codex adversarial pass, all blocking
silent / correct behavior of Tier 0:

1. AUTORESET-ONLY CONFIG WAS A NO-OP: the action gate in
   checkContextStatus bailed whenever ctx_handoff_threshold was
   undefined, so an operator who configured ONLY ctx_autoreset_threshold
   (the documented enablement) got observe-only mode. Gate now arms the
   monitor if any of the three thresholds are set.

2. BOOT TELEGRAMS AFTER TIER 0: the post-Tier-0 fresh session ran the
   normal cold-boot prompt which tells the agent to send "Booting up..."
   and "back online" messages. That violated the silent-auto-reset
   contract. Added .silent-restart marker armed by Tier 0 and consumed
   by agent-process.ts; when present, the boot prompt gets a
   SILENT AUTO-RESET override that suppresses both Telegram messages.
   Marker is unlinked after consumption so the effect lasts one restart.

3. MANUAL auto-compact-agent DID NOT ACTUALLY RESTART: writing
   .force-fresh + .restart-planned is not enough on its own. The daemon
   only consumes those markers at the next start(). CLI now sends IPC
   restart-agent to the daemon after arming markers, matching the
   self-restart pattern. --no-ipc flag preserves the old arm-only
   behavior for scripted use.

4. CROSS-AGENT ENV LEAK: when agent A ran auto-compact-agent for agent
   B, execFileSync inherited A's CTX_AGENT_DIR / CTX_AGENT_NAME, and
   snapshot-agent.sh prefers those env vars over the positional name —
   so A's memory marker and Telegram got written instead of B's.
   autoCompactAgent now scrubs those env vars and overrides
   CTX_AGENT_NAME to the target; CTX_AGENT_DIR is intentionally omitted
   so the script falls back to the positional-argument derivation.

autoCompactAgent also now writes .silent-restart alongside the restart
markers so the manual hatch matches Tier 0's silent contract.

Tests cover: env scrubbing (caller CTX vars do not leak to stub),
.silent-restart marker presence after silent auto-compact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Upstream PR: grandamenium#206

Fleet-wide silent auto-reset at configurable ctx_autoreset_threshold
(typical 55%). Fires BEFORE the existing 70/80 tiers; non-blocking
snapshot via spawn+detached; .silent-restart marker suppresses boot
Telegrams; cortextos bus auto-compact-agent manual hatch with IPC
restart-agent.

Two adversarial passes caught 7 bugs total; all fixed before merge:
blocking poll loop, cold-boot restart loop, clock skew, autoreset-only
config no-op, silent-mode Telegram leak, manual hatch no-restart,
cross-agent env leak.
…lection ingest

Root cause: ingestKnowledgeBase passed timeout:120000 to execFileSync. When
incremental ingest ran against a collection with 1000+ chunks, the python
mmrag.py child had to dedup existing doc IDs, call the Gemini embedding API
(network-bound, rate-limited), and upsert to ChromaDB. That work routinely
exceeds 2 minutes, so Node's timer fired and SIGTERM'd the healthy child.
Direct python invocation worked because no Node-imposed timer existed.

Fix: remove the default timeout (ingest is interactive, stdio:inherit, user
sees progress and can Ctrl-C). Add optional CORTEXTOS_KB_INGEST_TIMEOUT_MS
env override for CI/automation that needs to bound runtime. Read from the
merged env so .env / secrets.env overrides work the same way MMRAG_CONFIG
and other KB vars do.

Tests: 5 new vitest cases covering no-default-timeout, positive-number
override, invalid-value handling, merged-env override, and regression guard
against a simulated 5-minute subprocess.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on kb-ingest

Fixes SIGTERM kill on large (1000+ chunks) collection ingest. Root cause was
execFileSync timeout:120000 firing on healthy network-bound work; direct python
invocation worked because no Node timer. Adds optional CORTEXTOS_KB_INGEST_TIMEOUT_MS
env override for automation.

PR: grandamenium#208
…n Vault

New `cortextos bus publish-to-vault <file> --vault-dir <subdir>` command so
any agent can land strategic deliverables in the Vault with provenance
frontmatter (published_by, published_at, source_task, source_path, summary,
tags) and a wikilink back to the originating task. Bridges the gap between
ChromaDB-searchable agent output and Logan's daily Obsidian workflow.

- src/bus/vault.ts — core module with hardened path + source-file handling:
  * vault-dir containment: per-segment .. / . rejection, realpath check that
    the resolved target stays under the canonical vault root
  * source read: lstat preflight (rejects FIFO/device/dir), then a single
    openSync + fstat + bounded readSync so symlink swaps can't reach us
  * size cap 10 MB, NUL-byte binary heuristic in first 8 KB
  * collision-safe write: temp file + linkSync (EEXIST retries -v2, -v3, …)
    so two parallel publishers never clobber each other via rename TOCTOU
  * task-id requires task-dir + regular-file stat to block fabricated
    provenance from planted dirs / symlinks named <id>.json
- src/cli/bus.ts — wiring with --vault-dir / --task-id / --summary / --tags
  / --vault-root flags; resolves $VAULT_ROOT → org context.json →
  hardcoded default; emits output_published_to_vault event
- templates/agent/AGENTS.md — "Publishing to the Vault" section +
  output_published_to_vault row in the event-logging table
- tests/unit/bus/vault.test.ts — 32 tests covering happy path, collisions,
  frontmatter merge, and adversarial inputs (traversal, symlink escape,
  binary files, oversized files, TOCTOU pressure, fabricated task-ids)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Defends against a planted directory, symlink, or FIFO named
`<task_id>.json` in the taskDir. existsSync() returns true for all of
those, so the publish would record fabricated provenance. lstat +
isFile() closes the hole.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the "merged but never shipped" gap we hit today on 4 OAuth Edge
Functions. Adds two automations wired into git hooks, plus docs + tests.

- scripts/setup-upstream-pr.sh: one-time setup. Verifies gh auth, ensures
  fork exists, wires origin=fork / upstream=grandamenium/cortextos.
- scripts/auto-pr-upstream.sh: pre-push delegate. Opens a PR to upstream
  when a branch touches src/, dashboard/, templates/, .claude/hooks/, or
  scripts/. Idempotent; never blocks the push on any failure path.
- scripts/auto-deploy-supabase.sh: post-merge delegate. Deploys every
  supabase/functions/<name>/ touched by the merge via
  `supabase functions deploy <name> --project-ref baidaaansxrfdislmgyx`.
- scripts/hooks/pre-push: extended to schedule auto-PR-upstream in background.
- scripts/hooks/post-merge: new hook delegating to auto-deploy-supabase.
- scripts/setup-hooks.sh: installs both hooks.
- CONTRIBUTING.md: new "Contributing to upstream" section documenting the
  flow, kill switches, and failure modes.
- tests/unit/scripts/: 19 new integration tests covering the happy path and
  adversarial cases (unsafe branch names, missing upstream remote, behind
  upstream, gh not authed, network failure, duplicate PR, supabase CLI
  missing, AUTO_DEPLOY_SKIP kill switch, path-like function names, deleted
  function dirs, continue-on-failure for partial deploys). All fake binaries
  PATH-prepended — no real gh / supabase calls.

macOS bash 3.2 compatible (no mapfile/readarray). All failure paths exit 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tional now optional

Option B migration of the evaluate-experiment CLI. Previously, passing
`--score N` overwrote result_value AND baseline_value with the 1-10
rubric, discarding the actual measured number. The quantitative
workflow (pricing autoresearch) silently lost every measurement.

The original behavior had a purpose: qualitative metrics where the
agent passed 0 as a placeholder measuredValue and --score 7 as the
real value. Eight skill docs taught that pattern. A naive fix would
ship a new bug onto every qualitative cycle.

This PR resolves both:

- Add `score: number | null` to the Experiment interface; initialize
  to null on create; normalize to null on load for legacy JSONs that
  predate the field (loadExperiment + listExperiments).
- CLI `evaluate-experiment` `<value>` becomes optional. At least one
  of positional value or --score must be provided (throws otherwise).
  --score validates as integer 1-10 at the CLI boundary.
- evaluateExperiment(): when both value and --score are provided,
  result_value tracks the positional value and score lives in its own
  field. When only --score is provided (qualitative workflow), the
  score doubles as result_value so decision logic has a number to
  compare, and baseline_value rolls forward to the score on keep.
- results.tsv gets a new 'score' column (header + row); learnings.md
  adds a 'Score: N/10' line when present.
- Update CLI help text to reflect the new contract.
- Update 5 autoresearch skill docs (templates/agent, analyst,
  orchestrator + community/skills/autoresearch + community/agents/
  security) to drop the placeholder '0' — examples now use
  `evaluate-experiment <id> --score 7 --justification ...`.
- Add 5 regression tests covering: --score stored without overwriting
  measured value, score defaults to null, qualitative path with only
  --score, throw on both-missing, legacy-JSON normalization.

All 656 tests pass (43 test files, --exclude dashboard since the
unrelated comms/routes test failure predates this branch).

Found during 2026-04-22 autoresearch backfill of 6 pricing iterations;
the wrong behavior silently stored scores (5,8,9) where measured values
(76.9,80.6,92.4) should have landed. Backfilled files were patched by
hand at the time; this PR prevents recurrence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…re-field-v2

fix(bus): evaluate-experiment --score stored in own field; value positional now optional

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1840485bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const onlineMessage = isHandoffRestart || isSilentRestart
? ''
: ' After setting up crons, send a Telegram message to the user saying you are back online.';
<<<<<<< HEAD

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Resolve merge markers in daemon source

The merge conflict markers left in buildBootPrompt() make this TypeScript file unparsable (TS1185: Merge conflict marker encountered), which blocks tsc/builds and prevents shipping any runtime changes from this commit. This is a hard failure for all environments that compile or run the daemon code.

Useful? React with 👍 / 👎.

Comment thread ecosystem.config.js
args: '--instance ' + (process.env.CTX_INSTANCE_ID || "default"),
cwd: "/Users/loganbronstein/cortextos",
env: {
<<<<<<< HEAD

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove conflict markers from ecosystem config

Unresolved merge markers are committed directly in ecosystem.config.js, so Node cannot parse the PM2 config (Unexpected token '<<'). Any deployment path using pm2 start ecosystem.config.js will fail before the daemon can launch.

Useful? React with 👍 / 👎.

import { join } from 'path';
import { tmpdir } from 'os';
import { execSync } from 'child_process';
<<<<<<< HEAD

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clean merge markers from bus system test import

This test file still contains conflict markers around the import list, which makes the spec syntactically invalid and prevents Vitest from parsing/running it. Even after fixing production code, CI test execution will remain broken until these markers are removed.

Useful? React with 👍 / 👎.

loganbronstein added a commit that referenced this pull request Jun 19, 2026
…build-pin disclosure corrected + #2 collection-level fidelity

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018pcr1gUvn3fSvCoCxGYk56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant