Skip to content

feat(self-knowledge): session boot self-knowledge — vault secret names + operational facts injected at session start#811

Merged
JKHeadley merged 2 commits into
mainfrom
echo/session-boot-self-knowledge
Jun 5, 2026
Merged

feat(self-knowledge): session boot self-knowledge — vault secret names + operational facts injected at session start#811
JKHeadley merged 2 commits into
mainfrom
echo/session-boot-self-knowledge

Conversation

@JKHeadley

Copy link
Copy Markdown
Owner

ELI16

Your agent keeps asking you to re-send credentials it already has, and keeps not knowing about tools it has used dozens of times. The data was never lost — the encrypted vault and the logged-in browser seat are right there on disk. What's missing is awareness: every new session wakes up like a person with a safe in their basement and no memory of owning a safe.

This PR adds a small "what I already have" note to the context every session receives at startup: the names (never the values) of the secrets in the agent's vault, plus any operational facts the agent has recorded about its machine (like "your logged-in Telegram test seat is the default playwright profile at this path"). One rule rides along: a secret named here is in the vault — fetch it with the new hardened secret-get.mjs script (value pipes straight into the consuming command, never echoed) instead of asking the user to re-send it, unless it's actually invalid.

Safety, in plain terms: no secret value ever appears anywhere; names and facts are sanitized so a malicious entry can't smuggle instructions into the boot context; a vault that won't unlock says "don't touch it, tell the operator" instead of pretending to be empty; tests can no longer touch the real macOS keychain (the structural guard closes the incident class from 2026-06-05); and the feature ships dark on the fleet, live on Echo — the live-fleet flip is a tracked one-line follow-up (CMT-1053) that rides #800's merge or your explicit "approve, live".

What's in the box

  • src/core/BootSelfKnowledge.ts — block builder: shared secretKeyPaths() derivation, depth-2 collapse, sanitize/clamp/alphabetize/cap, byte-bounded with actionable truncation markers, decrypt-failure honesty (exists-check → one retry → hands-off warning, never an empty-vault lie), module-level names cache keyed on vault path + (mtimeMs, size).
  • GET /self-knowledge/session-context (?full=1) behind the developmentAgent gate; decrypt failure is a 200 with the warning block (the hook's curl -sf would swallow a 5xx and hide it).
  • POST/DELETE /self-knowledge/facts — agent-driven facts writer (auto-stamped {fact, updatedAt, machine}, dup/cap/ambiguity 409s, expect-guarded delete) through a new atomic temp+rename config write.
  • Session-start hook: one fail-open fetch block (curl -sf --max-time 4 --connect-timeout 1, header-only Bearer), after org-intent + preferences; always-overwrite delivery.
  • secret-get.mjs — hardened vault retrieval (stdout pipe-only, stderr diagnostics, value-silent error paths); shipped via migrateScripts + init.
  • MasterKeyManager VITEST constructor guard — keychain structurally unreachable from tests.
  • Full Migration Parity: migrateConfig defaults, CLAUDE.md template + migrateClaudeMd, hook always-overwrite. Agent Awareness section included.

Verification

  • Spec converged in 3 iterations: 5 internal reviewers + Standards-Conformance Gate + a real codex-cli:gpt-5.5 external pass every round (docs/specs/session-boot-self-knowledge.md, report in docs/specs/reports/).
  • 36 new tests across 4 files (unit / integration / e2e-lifecycle / migration-parity), green; full suite green; lint green.
  • Post-merge live verification (test-as-self, hard gate per the EXO 3.0 requirement): drive a fresh session over real Telegram via the Playwright seat with a task requiring the stored GitHub PAT — it must complete it via secret-get.mjs without re-asking. Will run after deploy and report in topic 19437.

Collateral findings filed to the framework-issues ledger: sync-status-decrypt-fail-reads-empty, session-start-hook-uncapped-curls.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
instar Ready Ready Preview, Comment Jun 5, 2026 5:13pm

Request Review

@JKHeadley JKHeadley force-pushed the echo/session-boot-self-knowledge branch from 42fb7eb to 5bbf3fa Compare June 5, 2026 08:28
@JKHeadley JKHeadley closed this Jun 5, 2026
@JKHeadley JKHeadley reopened this Jun 5, 2026
@JKHeadley JKHeadley force-pushed the echo/session-boot-self-knowledge branch from 5bbf3fa to f4bb3d2 Compare June 5, 2026 16:33
JKHeadley added a commit that referenced this pull request Jun 5, 2026
…rvive compaction (+ structural parity test) (#848)

New standard, earned from PR #811's design review: the boot self-knowledge
block survived three convergence rounds boot-only until the operator asked
"sessions last days; won't this be forgotten after compaction?" — the whole
session-context injector class (org-intent, preferences) carried the same
silent gap.

Rule: whatever a session must know at message one, it must still know after
compaction — every session-start injector ships its compaction-recovery twin.
Enforcement: tests/unit/session-context-compaction-parity.test.ts asserts
every */session-context fetch in the session-start hook appears in the
compaction-recovery hook, with a shrink-only allowlist for the two legacy
violators (tracked: framework-issue
session-context-injectors-lack-compaction-parity).

Ratification: proposed to Justin (topic 19437, 2026-06-05) — his suggestion.

Co-authored-by: Instar Agent (echo) <echo@instar.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…s + operational facts injected at session start

Closes the secret re-ask loop and the unknown-channel loop (topic 19437): an
agent session now boots KNOWING what its own infrastructure holds.

- src/core/BootSelfKnowledge.ts: bounded <session-self-knowledge> block —
  vault secret NAMES (never values; shared secretKeyPaths derivation, depth-2
  collapse, sanitized/clamped/alphabetical/capped, actionable truncation
  marker) + self-asserted operational facts (stamped {fact,updatedAt,machine}).
  Decrypt-failure honesty: exists-check, one retry, hands-off warning — never
  an empty-vault lie. Module names-cache keyed on vault path + (mtimeMs,size).
- Routes: GET /self-knowledge/session-context (?full=1) behind the
  developmentAgent gate (enabled ?? !!developmentAgent — dark fleet / live
  dev-agent; live flip tracked as CMT-1053); POST/DELETE
  /self-knowledge/facts (validated, dup/cap/ambiguity 409s, expect-guarded
  delete) via the new writeConfigAtomic() temp+rename helper. Decrypt failure
  is a 200 with the warning block, never a 500 (curl -sf swallows 5xx).
- Session-start hook: one fail-open fetch block (curl -sf --max-time 4
  --connect-timeout 1, header-only Bearer), placed after org-intent +
  preferences; always-overwrite delivery via migrateHooks.
- secret-get.mjs: hardened vault retrieval (value→stdout pipe-only,
  names→stderr, value-silent on every error path) — the read path the block's
  guidance names; shipped via migrateScripts + init.
- MasterKeyManager: VITEST constructor guard forces the file key — no test
  can ever read or overwrite the machine-global keychain master key again
  (closes the 2026-06-05 bifurcated-master-key incident class).
- Config surface (selfKnowledge.*) + ConfigDefaults + migrateConfig backfill;
  CLAUDE.md template section + migrateClaudeMd parity.
- 36 tests across 4 files (unit/integration/e2e/migration), all green; spec
  converged (3 iterations, codex-cli:gpt-5.5 cross-model every round).

Spec: docs/specs/session-boot-self-knowledge.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ery registry + framework-shadow markers

CI's feature-delivery-completeness guard caught the new section untracked:
now registered in featureSections AND mirrored to the shadow-capability
markers so Codex/Gemini agents learn the capability too (an unshadowed
capability gets improvised around — the Secret Drop lesson).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JKHeadley JKHeadley merged commit 9768e0e into main Jun 5, 2026
20 checks passed
JKHeadley pushed a commit that referenced this pull request Jun 6, 2026
… start (Know Your Principal #898, increment 2c)

Adds the READ side of the operator binding: the session-start hook now fetches
/topic-operator/session-context?topicId=$INSTAR_TELEGRAM_TOPIC (the route shipped
in #906) and injects the <topic-operator> block when the topic has a verified
operator — so the agent reasons with its authenticated principal from message one
and never seats a content name in the operator's chair (the "Caroline" fix).

- PostUpdateMigrator.getHookContent('session-start'): one additive fetch-block,
  modeled byte-for-byte on the ORG-INTENT and AUTO-LEARNED-PREFERENCES blocks.
- Compaction Parity (PR #811 standard): the SAME fetch is wired into
  getCompactionRecovery() so the verified operator is re-injected after a context
  reset — thematically load-bearing here, since losing operator awareness
  post-compaction is exactly the identity gap this feature closes.
- Fail-open, three guards: runs only when $INSTAR_TELEGRAM_TOPIC + $PORT + $TOKEN
  are set; unbound topic / store-503 / unreachable injects nothing.
- Cannot seat anyone — only surfaces the operator the store already verified.

Migration parity: the instar/ session-start + compaction-recovery hooks are both
rewritten on every update, so existing agents get the blocks automatically.

Tests: 3 Tier-3 E2E lifecycle tests + the session-context-compaction-parity unit
test (now green — the new injector has its compaction twin, not an allowlist
entry). Analog hook suites stay green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JKHeadley added a commit that referenced this pull request Jun 6, 2026
… start (Know Your Principal #898, increment 2c) (#908)

Adds the READ side of the operator binding: the session-start hook now fetches
/topic-operator/session-context?topicId=$INSTAR_TELEGRAM_TOPIC (the route shipped
in #906) and injects the <topic-operator> block when the topic has a verified
operator — so the agent reasons with its authenticated principal from message one
and never seats a content name in the operator's chair (the "Caroline" fix).

- PostUpdateMigrator.getHookContent('session-start'): one additive fetch-block,
  modeled byte-for-byte on the ORG-INTENT and AUTO-LEARNED-PREFERENCES blocks.
- Compaction Parity (PR #811 standard): the SAME fetch is wired into
  getCompactionRecovery() so the verified operator is re-injected after a context
  reset — thematically load-bearing here, since losing operator awareness
  post-compaction is exactly the identity gap this feature closes.
- Fail-open, three guards: runs only when $INSTAR_TELEGRAM_TOPIC + $PORT + $TOKEN
  are set; unbound topic / store-503 / unreachable injects nothing.
- Cannot seat anyone — only surfaces the operator the store already verified.

Migration parity: the instar/ session-start + compaction-recovery hooks are both
rewritten on every update, so existing agents get the blocks automatically.

Tests: 3 Tier-3 E2E lifecycle tests + the session-context-compaction-parity unit
test (now green — the new injector has its compaction twin, not an allowlist
entry). Analog hook suites stay green.

Co-authored-by: Instar Agent (echo) <echo@instar.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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