feat(triage): wire contextual (digested comms) into ChittyTriage intake spine#122
feat(triage): wire contextual (digested comms) into ChittyTriage intake spine#122chitcommit wants to merge 2 commits into
Conversation
…ke spine
ChittyTriage arm W4c: digested cross-channel comms (the "contextual" store,
Neon ChittyLedger-Messaging) -> classify via chittyrouter -> chittycommand's
sovereign cc_ tables, candidate->verified lifecycle, per-edge Two-Space gate.
New upstream:
- src/lib/contextual-ingest.ts — pulls amount+payee+date signal from the
contextual store (separate Neon project, read via CONTEXTUAL_DATABASE_URL),
classifies each candidate, and writes:
* cc_intents (intent_type='contextual_ingest', idempotent on the contextual
message id, mirrors the proven roux_ingest ladder) — status stays on the
executor enum; candidate provenance carried in payload.
* cc_obligations — status='candidate', source='contextual', source_ref,
confidence, sensitivity. Deduped (partial unique index on source_ref).
* cc_recommendations — same provenance, action_type='review_candidate'.
Conflicts (inferred amount disagrees with an existing record) never
auto-resolve: they raise a chittyagent-tasks reconciliation item via the
real tasks.chitty.cc/api/v1/tasks path; the existing row is NOT overwritten.
Legal/privileged signal (legal_document entity, case #287/#239/2024D007847)
-> sensitivity='legalink' + privileged intent so it does not bleed to
Business surfaces.
Classifier: attempts chittyrouter intelligentRoute (POST /process); the
deployed edge does not expose it (404) and /agents/triage/classify returns a
deterministic stub, so it falls back to the SAME model chittyrouter runs
(@cf/meta/llama-3.1-8b-instruct-fast) inline via env.AI, then a deterministic
feature fallback. All paths are AI/inference -> rows land as 'candidate'
(spine rule: verified only with >=1 NON-AI source).
Wiring:
- integrations.ts: tasksClient (chittyagent-tasks) for conflict escalation.
- cron.ts: Phase 3.5 runs the ingest before triage so candidates are scored
the same tick (skips cleanly when CONTEXTUAL_DATABASE_URL is unset).
- routes/triage.ts: POST /api/triage/contextual/ingest on-demand trigger.
- index.ts: CONTEXTUAL_DATABASE_URL, CHITTYAGENT_TASKS_URL/_TOKEN env.
Schema (migration 0019): provenance + sensitivity columns on
cc_obligations / cc_recommendations, dedup + idempotency indexes.
Provenance/sensitivity columns added: source, source_ref, confidence (oblig),
sensitivity on both; partial unique dedup index on cc_obligations.source_ref;
contextual_ingest idempotency index on cc_intents.
Verified on a Neon branch of production (cool-bar-13270800 /
br-royal-morning-ak3r7dxt) against the REAL contextual store
(delicate-moon-28755675). Production lacks cc_intents/cc_goals/cc_plans
(meta-orchestrator migrations 0002+ never applied to the live DB) — the
sovereign write is gated, so build+verify ran on the branch (see PR body).
Verification (real rows, no mocks):
- before -> after: cc_intents(contextual_ingest) 0->3, cc_obligations(contextual)
0->1, cc_recommendations(contextual) 0->1, agent_tasks.tasks(reconciliation,
chittycommand) 0->1.
- Sample inferred obligation: Kris $1000, status=candidate, source=contextual,
source_ref=ctx-msg-16884, confidence=0.665, sensitivity=legalink, from a real
openphone lease-addendum message.
- Conflict: existing manual Kris $500 vs inferred $1000 -> real reconciliation
task in agent_tasks.tasks (assigned chittyagent-command); existing row NOT
overwritten (both coexist).
- Idempotency: re-running the obligation insert returns 0 new rows.
- Sensitivity: legal-tagged Kris -> legalink across intent/obligation/task;
payee-null bills -> business.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
chittycommand | 77b28af | Jun 14 2026, 12:07 AM |
|
📝 WalkthroughWalkthroughAdds a contextual ingest pipeline that reads financial obligation signals from a separate "contextual" database, classifies them via chittyrouter/AI/heuristics, and writes idempotent ChangesContextual Ingest Pipeline
Sequence Diagram(s)sequenceDiagram
participant CronJob
participant ingestContextual
participant ContextualDB
participant chittyrouter
participant AI as env.AI
participant TriageDB as cc_intents / cc_obligations / cc_recommendations
participant tasksClient as chittyagent-tasks
CronJob->>ingestContextual: Phase 3.5 — ingestContextual(env, sql, {limit:50})
ingestContextual->>ContextualDB: fetchContextualCandidates (amount-entity messages)
ContextualDB-->>ingestContextual: ContextualCandidate[]
loop each candidate
ingestContextual->>chittyrouter: POST /process (classifyCandidate)
alt router unavailable
ingestContextual->>AI: run(model, prompt) → compact JSON
alt AI parse fails
ingestContextual->>ingestContextual: deterministic keyword/legal heuristics
end
end
chittyrouter-->>ingestContextual: ClassificationResult
ingestContextual->>TriageDB: INSERT cc_intents ON CONFLICT DO NOTHING
alt intent is new
ingestContextual->>TriageDB: SELECT latest obligation for payee
alt materially different amount
ingestContextual->>tasksClient: createTask (reconciliation)
tasksClient-->>ingestContextual: AgentTaskResult
end
ingestContextual->>TriageDB: INSERT cc_obligations ON CONFLICT (source_ref) DO NOTHING
ingestContextual->>TriageDB: INSERT cc_recommendations linked to obligation
end
end
ingestContextual-->>CronJob: ContextualIngestResult
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
To use Codex here, create a Codex account and connect to github. |
| res.end(JSON.stringify({ success: true, data: row })); | ||
| } catch (err) { | ||
| console.error('[local-tasks] insert failed:', err); | ||
| res.writeHead(500); res.end(JSON.stringify({ success: false, error: String(err) })); |
| res.end(JSON.stringify({ success: true, data: row })); | ||
| } catch (err) { | ||
| console.error('[local-tasks] insert failed:', err); | ||
| res.writeHead(500); res.end(JSON.stringify({ success: false, error: String(err) })); |
…nistic amount createRouxIngestIntentIdempotent hardcodes ON CONFLICT/re-fetch on intent_type='roux_ingest', so routing contextual_ingest through it never deduped (and with the 0019 index present, raised a unique violation on the second insert) — unbounded goal/plan/intent fan-out per cron tick. - Add createContextualIngestIntentIdempotent: ON CONFLICT arbiter predicate and re-fetch both filter intent_type='contextual_ingest', targeting the 0019 cc_intents_contextual_ingest_message_id_uidx index. - contextual-ingest.ts uses the new helper. - fetchContextualCandidates collapses to ONE candidate per message (DISTINCT ON message_id, MAX amount) so the deduped obligation is deterministic regardless of row order (a message mentioning several amounts no longer yields a nondeterministic obligation amount). Re-verified on branch: two inserts of a fresh contextual message_id -> exactly 1 intent row (was: unique violation / unbounded). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
To use Codex here, create a Codex account and connect to github. |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@migrations/0019_contextual_provenance.sql`:
- Around line 24-30: The `confidence` column in the cc_obligations table and the
`sensitivity` columns in both cc_obligations and cc_recommendations tables lack
database-level constraints to enforce valid values. Add CHECK constraints to the
ALTER TABLE statements: for the `confidence` column in cc_obligations, add a
CHECK constraint ensuring the value is between 0 and 1; for the `sensitivity`
columns in both tables, add a CHECK constraint that restricts values to valid
domain options (such as 'business' and 'privileged'). These constraints should
be added as part of the respective ALTER TABLE ADD COLUMN statements or in
separate ALTER TABLE ADD CONSTRAINT statements to ensure data integrity at the
schema boundary.
In `@scripts/verify-contextual-ingest.mjs`:
- Line 66: The HTTP error response at the 500 status code handler is exposing
raw exception details by including `String(err)` directly in the client
response, which is a security vulnerability. Replace the `error: String(err)` in
the JSON response with a generic error message like `error: "Internal server
error"`, and instead log the actual error details (using `String(err)` or
similar) to the server logs for debugging purposes. This way, sensitive error
information stays on the server while clients receive a safe, generic response.
- Line 25: The import statement in verify-contextual-ingest.mjs imports a
TypeScript file directly, but the script is documented to run with plain node
without any TypeScript tooling configured. Either change the import statement at
line 25 to reference a compiled .js version of contextual-ingest instead of the
.ts file, or configure a TypeScript loader (such as tsx, ts-node, or Node's
--experimental-strip-types flag) to enable the script to load TypeScript modules
at runtime. Ensure the chosen approach aligns with the project's build setup and
the documented invocation method.
In `@src/lib/contextual-ingest.ts`:
- Around line 300-301: The sourceRef key constructed at the location where it
uses only message_id is too coarse-grained and treats multiple amount entities
from the same message as duplicates, causing valid obligations to be dropped.
You need to decide between two approaches: either collapse to one amount entity
per message in the SQL query before looping (if only one obligation per message
is intended), or include the amount-entity identifier in the sourceRef string
alongside the message_id (e.g., ctx-msg-<message_id>-amt-<entity_id> or similar
unique entity reference) to ensure each distinct amount entity gets its own
unique sourceRef. Once you choose your approach, apply it consistently across
all locations where sourceRef is constructed and ensure the intent key and
unique index constraints are also aligned with this decision to prevent
unintended deduplication.
In `@src/lib/integrations.ts`:
- Around line 1092-1096: The tasksClient requests in src/lib/integrations.ts are
missing the required X-Source-Service header that other internal integrations
include. Add the header `X-Source-Service: chittycommand` to the headers object
alongside the existing Content-Type, Authorization, and X-ChittyOS-Caller
headers to comply with the coding guidelines for all outbound service calls in
this file.
In `@src/routes/triage.ts`:
- Around line 238-242: The validation for the limit parameter in the request
body currently accepts non-integer numbers (like 1.5 or 2.7) because the type
check only verifies typeof body.limit === 'number'. Replace the existing
condition to use Number.isInteger() to ensure the limit is an actual integer
between 1 and 500. When the limit fails validation (not an integer or outside
the range), return a 400 response with an appropriate error message instead of
silently defaulting to 50.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 83e5219f-7d79-4f79-80c9-8bb6673ccc0c
📒 Files selected for processing (7)
migrations/0019_contextual_provenance.sqlscripts/verify-contextual-ingest.mjssrc/index.tssrc/lib/contextual-ingest.tssrc/lib/cron.tssrc/lib/integrations.tssrc/routes/triage.ts
| ALTER TABLE cc_obligations ADD COLUMN IF NOT EXISTS confidence numeric; | ||
| ALTER TABLE cc_obligations ADD COLUMN IF NOT EXISTS sensitivity text NOT NULL DEFAULT 'business'; | ||
|
|
||
| -- ── cc_recommendations: provenance so triage output is traceable to comms ── | ||
| ALTER TABLE cc_recommendations ADD COLUMN IF NOT EXISTS source text; | ||
| ALTER TABLE cc_recommendations ADD COLUMN IF NOT EXISTS source_ref text; | ||
| ALTER TABLE cc_recommendations ADD COLUMN IF NOT EXISTS sensitivity text NOT NULL DEFAULT 'business'; |
There was a problem hiding this comment.
Enforce DB-level domain constraints for confidence and sensitivity.
confidence is documented as 0..1 and sensitivity drives business vs privileged gating, but both columns currently accept invalid values. Add CHECK constraints to keep persisted state safe and enforceable at the schema boundary.
Suggested migration patch
ALTER TABLE cc_obligations ADD COLUMN IF NOT EXISTS source text;
ALTER TABLE cc_obligations ADD COLUMN IF NOT EXISTS source_ref text;
ALTER TABLE cc_obligations ADD COLUMN IF NOT EXISTS confidence numeric;
ALTER TABLE cc_obligations ADD COLUMN IF NOT EXISTS sensitivity text NOT NULL DEFAULT 'business';
+ALTER TABLE cc_obligations
+ ADD CONSTRAINT cc_obligations_confidence_chk
+ CHECK (confidence IS NULL OR (confidence >= 0 AND confidence <= 1)),
+ ADD CONSTRAINT cc_obligations_sensitivity_chk
+ CHECK (sensitivity IN ('business', 'legalink'));
@@
ALTER TABLE cc_recommendations ADD COLUMN IF NOT EXISTS source text;
ALTER TABLE cc_recommendations ADD COLUMN IF NOT EXISTS source_ref text;
ALTER TABLE cc_recommendations ADD COLUMN IF NOT EXISTS sensitivity text NOT NULL DEFAULT 'business';
+ALTER TABLE cc_recommendations
+ ADD CONSTRAINT cc_recommendations_sensitivity_chk
+ CHECK (sensitivity IN ('business', 'legalink'));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@migrations/0019_contextual_provenance.sql` around lines 24 - 30, The
`confidence` column in the cc_obligations table and the `sensitivity` columns in
both cc_obligations and cc_recommendations tables lack database-level
constraints to enforce valid values. Add CHECK constraints to the ALTER TABLE
statements: for the `confidence` column in cc_obligations, add a CHECK
constraint ensuring the value is between 0 and 1; for the `sensitivity` columns
in both tables, add a CHECK constraint that restricts values to valid domain
options (such as 'business' and 'privileged'). These constraints should be added
as part of the respective ALTER TABLE ADD COLUMN statements or in separate ALTER
TABLE ADD CONSTRAINT statements to ensure data integrity at the schema boundary.
| */ | ||
| import http from 'node:http'; | ||
| import { neon } from '@neondatabase/serverless'; | ||
| import { ingestContextual } from '../src/lib/contextual-ingest.ts'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check how this script is expected to run and whether TS runtime support is configured.
node -v
rg -n -C3 "verify-contextual-ingest|tsx|ts-node|--loader|--import|experimental-strip-types" package.json || true
rg -n -C3 "Usage: node scripts/verify-contextual-ingest\\.mjs|import \\{ ingestContextual \\} from '../src/lib/contextual-ingest\\.ts'" scripts/verify-contextual-ingest.mjsRepository: chittyos/chittycommand
Length of output: 628
🏁 Script executed:
cat package.json | head -100Repository: chittyos/chittycommand
Length of output: 1420
🏁 Script executed:
find . -maxdepth 2 -name "tsconfig.json" -o -name "tsconfig*.json" | head -20Repository: chittyos/chittycommand
Length of output: 101
🏁 Script executed:
rg -n "build|compile|scripts" package.json -A 5 -B 1 | head -50Repository: chittyos/chittycommand
Length of output: 863
🏁 Script executed:
ls -la src/lib/contextual-ingest.* 2>/dev/null || echo "No file found"Repository: chittyos/chittycommand
Length of output: 145
🏁 Script executed:
find . -maxdepth 2 -name "wrangler.toml" -o -name "wrangler.json" | xargs cat 2>/dev/null | head -50Repository: chittyos/chittycommand
Length of output: 158
🏁 Script executed:
ls -la dist/ 2>/dev/null || echo "No dist directory"Repository: chittyos/chittycommand
Length of output: 84
🏁 Script executed:
ls -la build/ 2>/dev/null || echo "No build directory"Repository: chittyos/chittycommand
Length of output: 85
🏁 Script executed:
cat tsconfig.json | head -50Repository: chittyos/chittycommand
Length of output: 636
🏁 Script executed:
cat -n scripts/verify-contextual-ingest.mjs | head -40Repository: chittyos/chittycommand
Length of output: 2260
🏁 Script executed:
grep -r "verify-contextual-ingest" . --include="*.md" --include="*.json" --include="*.sh" 2>/dev/null | head -20Repository: chittyos/chittycommand
Length of output: 48
🏁 Script executed:
# Check if there's a compiled JavaScript version of the TypeScript file anywhere
find . -name "contextual-ingest.js" -o -name "contextual-ingest.mjs" 2>/dev/nullRepository: chittyos/chittycommand
Length of output: 48
🏁 Script executed:
# Check if there's any build process that might compile scripts directory
rg -n "scripts/" package.json wrangler.toml 2>/dev/null || trueRepository: chittyos/chittycommand
Length of output: 145
🏁 Script executed:
# Check if there's any loader or flag configuration in wrangler.toml or environment
cat wrangler.tomlRepository: chittyos/chittycommand
Length of output: 112
🏁 Script executed:
# Check if there's a compiled version in the scripts directory or elsewhere
find . -type f -name "*.mjs" -o -name "*.js" | grep -E "(scripts|src/lib)" | head -20Repository: chittyos/chittycommand
Length of output: 105
🏁 Script executed:
# Check what Node.js experimental features might be enabled
grep -r "experimental" . --include="package.json" --include="wrangler.toml" --include=".npmrc" 2>/dev/null || echo "No experimental config found"Repository: chittyos/chittycommand
Length of output: 95
🏁 Script executed:
# Verify if this script is actually meant to be run with node or with a different tool
file scripts/verify-contextual-ingest.mjsRepository: chittyos/chittycommand
Length of output: 111
Importing .ts directly from .mjs will fail under the documented invocation.
Line 25 imports ../src/lib/contextual-ingest.ts, but the script's documentation specifies Usage: node scripts/verify-contextual-ingest.mjs without any TypeScript tooling. The project's build setup confirms this:
tsconfig.jsonexplicitly excludes thescripts/directory and hasnoEmit: true(type-check only)- No compiled
.jsversion ofcontextual-ingest.tsexists - No TypeScript loader is configured (no
--loader,--experimental-strip-types, ortsx)
Running the script with plain node will fail at the import statement. Either wire a TypeScript loader (via tsx, ts-node, or Node's --experimental-strip-types flag with Node 18.19+), or point the import to a compiled .js file.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/verify-contextual-ingest.mjs` at line 25, The import statement in
verify-contextual-ingest.mjs imports a TypeScript file directly, but the script
is documented to run with plain node without any TypeScript tooling configured.
Either change the import statement at line 25 to reference a compiled .js
version of contextual-ingest instead of the .ts file, or configure a TypeScript
loader (such as tsx, ts-node, or Node's --experimental-strip-types flag) to
enable the script to load TypeScript modules at runtime. Ensure the chosen
approach aligns with the project's build setup and the documented invocation
method.
| res.end(JSON.stringify({ success: true, data: row })); | ||
| } catch (err) { | ||
| console.error('[local-tasks] insert failed:', err); | ||
| res.writeHead(500); res.end(JSON.stringify({ success: false, error: String(err) })); |
There was a problem hiding this comment.
Avoid returning raw exception text in HTTP error responses.
Line 66 exposes String(err) to the client response. Keep details in server logs and return a generic error payload instead.
🔒 Suggested fix
- res.writeHead(500); res.end(JSON.stringify({ success: false, error: String(err) }));
+ res.writeHead(500, { 'content-type': 'application/json' });
+ res.end(JSON.stringify({ success: false, error: 'internal_error' }));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| res.writeHead(500); res.end(JSON.stringify({ success: false, error: String(err) })); | |
| res.writeHead(500, { 'content-type': 'application/json' }); | |
| res.end(JSON.stringify({ success: false, error: 'internal_error' })); |
🧰 Tools
🪛 GitHub Check: CodeQL
[warning] 66-66: Exception text reinterpreted as HTML
Exception text is reinterpreted as HTML without escaping meta-characters.
[warning] 66-66: Information exposure through a stack trace
This information exposed to the user depends on stack trace information.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/verify-contextual-ingest.mjs` at line 66, The HTTP error response at
the 500 status code handler is exposing raw exception details by including
`String(err)` directly in the client response, which is a security
vulnerability. Replace the `error: String(err)` in the JSON response with a
generic error message like `error: "Internal server error"`, and instead log the
actual error details (using `String(err)` or similar) to the server logs for
debugging purposes. This way, sensitive error information stays on the server
while clients receive a safe, generic response.
Source: Linters/SAST tools
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${token}`, | ||
| 'X-ChittyOS-Caller': 'chittycommand', | ||
| }, |
There was a problem hiding this comment.
Add X-Source-Service: chittycommand to tasksClient requests.
tasksClient currently omits the required source-service header used by other internal integrations.
Suggested patch
const res = await fetch(`${baseUrl}/api/v1/tasks`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
+ 'X-Source-Service': 'chittycommand',
'X-ChittyOS-Caller': 'chittycommand',
},As per coding guidelines, all outbound service calls in src/lib/integrations.ts must include X-Source-Service: chittycommand.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${token}`, | |
| 'X-ChittyOS-Caller': 'chittycommand', | |
| }, | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${token}`, | |
| 'X-Source-Service': 'chittycommand', | |
| 'X-ChittyOS-Caller': 'chittycommand', | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/integrations.ts` around lines 1092 - 1096, The tasksClient requests
in src/lib/integrations.ts are missing the required X-Source-Service header that
other internal integrations include. Add the header `X-Source-Service:
chittycommand` to the headers object alongside the existing Content-Type,
Authorization, and X-ChittyOS-Caller headers to comply with the coding
guidelines for all outbound service calls in this file.
Source: Coding guidelines
| let limit = 50; | ||
| try { | ||
| const body = (await c.req.json().catch(() => ({}))) as { limit?: number }; | ||
| if (typeof body.limit === 'number' && body.limit > 0 && body.limit <= 500) limit = body.limit; | ||
| } catch { /* default */ } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify whether this endpoint uses zValidator/Zod schema and whether a dedicated validator exists.
rg -n -C3 "triageRoutes\\.post\\('/contextual/ingest'|zValidator\\('json'|limit must be" src/routes/triage.ts
fd -i validators.ts
rg -n -C3 "contextual.*ingest|triage.*contextual|limit" src/lib/validators.ts || trueRepository: chittyos/chittycommand
Length of output: 1565
🏁 Script executed:
sed -n '230,250p' src/routes/triage.tsRepository: chittyos/chittycommand
Length of output: 727
🏁 Script executed:
rg -n "zValidator|ingestContextual" src/routes/triage.ts | head -20Repository: chittyos/chittycommand
Length of output: 198
🏁 Script executed:
rg -n "contextual.*Schema|ingest.*Schema" src/lib/validators.tsRepository: chittyos/chittycommand
Length of output: 48
🏁 Script executed:
rg -n "export.*ingestContextual|function ingestContextual" src/lib/contextual-ingest.tsRepository: chittyos/chittycommand
Length of output: 110
🏁 Script executed:
rg -n -A5 "triageRoutes\.post" src/routes/triage.ts | head -40Repository: chittyos/chittycommand
Length of output: 1302
🏁 Script executed:
rg -n "zValidator" src/routes/Repository: chittyos/chittycommand
Length of output: 48
🏁 Script executed:
rg -n "import.*zValidator" src/Repository: chittyos/chittycommand
Length of output: 48
Use Number.isInteger() to validate limit and reject non-integers with a 400 response.
Line 238–242 currently accepts non-integer numbers (e.g., 1.5, 2.7) because the type check typeof body.limit === 'number' does not distinguish integers from floats. Invalid or missing payloads silently fall back to the default 50, weakening API correctness.
🔧 Suggested fix
let limit = 50;
- try {
- const body = (await c.req.json().catch(() => ({}))) as { limit?: number };
- if (typeof body.limit === 'number' && body.limit > 0 && body.limit <= 500) limit = body.limit;
- } catch { /* default */ }
+ const body = (await c.req.json().catch(() => ({}))) as { limit?: unknown };
+ if (body.limit !== undefined) {
+ const parsed = Number(body.limit);
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 500) {
+ return c.json({ error: 'limit must be an integer between 1 and 500' }, 400);
+ }
+ limit = parsed;
+ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/routes/triage.ts` around lines 238 - 242, The validation for the limit
parameter in the request body currently accepts non-integer numbers (like 1.5 or
2.7) because the type check only verifies typeof body.limit === 'number'.
Replace the existing condition to use Number.isInteger() to ensure the limit is
an actual integer between 1 and 500. When the limit fails validation (not an
integer or outside the range), return a 400 response with an appropriate error
message instead of silently defaulting to 50.
Source: Coding guidelines
What
Wires the contextual (digested cross-channel comms) store into ChittyTriage — the intake arm of chittycommand. Digested signal → classify via chittyrouter → chittycommand's sovereign
cc_tables, on the canonicalcandidate → verified → supersededlifecycle with a per-edge Two-Space sensitivity gate.This is ChittyTriage arm W4c of the projection spine (
contextual → chittyrouter → chittycommand integrations/triage).How it follows the canon
cc_intentsrows are created via the samecreateGoal/createPlan/createRouxIngestIntentIdempotentchain the Workspace-Studioroux_ingestpath uses, withintent_type='contextual_ingest'and an idempotency index mirroring migration 0017.status='candidate', neververified. Every inferred row is AI/inference-derived, so per the spine rule (verified requires ≥1 NON-AI source) obligations/recommendations land ascandidate.cc_intents.statusis left on the executor enum (pending) — the candidate marker rides inpayload/columns, so the executor state machine is untouched.source='contextual',source_ref(contextual message id, e.g.ctx-msg-16884),confidence, andsensitivity.cc_obligations.source_ref(contextual only) +ON CONFLICT DO NOTHING.chittyagent-tasksreconciliation item (tasks.chitty.cc/api/v1/tasks, assignedchittyagent-command) and leave the existing row intact; the candidate is still persisted so the disagreement is visible.legal_documententity, or case refs #287/#239/2024D007847) →sensitivity='legalink'+ privileged intent, so it does not bleed to Business surfaces.Classifier / intelligentRoute intake path
Attempts chittyrouter's
intelligentRoutesurface (POST /process). Empirically the deployed edge does not expose it (404),/ai/routeis the known 500, and/agents/triage/classifyreturns a deterministic stub (fallback:true, 0.5). Per the task's sanctioned fallback, the module then classifies inline with the same model chittyrouter runs (@cf/meta/llama-3.1-8b-instruct-fastviaenv.AI), then a deterministic feature fallback. The path used is recorded per-row inclassifier_via.Files
src/lib/contextual-ingest.ts— the upstream + ingest pipeline.migrations/0019_contextual_provenance.sql— provenance/sensitivity columns + dedup/idempotency indexes.src/lib/integrations.ts—tasksClient(chittyagent-tasks).src/lib/cron.ts— Phase 3.5 (runs before triage; skips ifCONTEXTUAL_DATABASE_URLunset).src/routes/triage.ts—POST /api/triage/contextual/ingeston-demand trigger.src/index.ts—CONTEXTUAL_DATABASE_URL,CHITTYAGENT_TASKS_URL/_TOKEN.scripts/verify-contextual-ingest.mjs— real verification harness (runs the actual module).Production
cool-bar-13270800(ChittyCommand) does not havecc_intents/cc_goals/cc_plans— the meta-orchestrator migrations (0002+) were never applied to the live DB (confirmed: the live DB is at pre-0002 state; Hyperdrive6f6cba43…originep-young-rain-ak3jf326↔ projectcool-bar-13270800). Writing the spine to the sovereign DB is therefore gated. Per policy, build + verification ran on a Neon branch of production (br-royal-morning-ak3r7dxt) with the missing meta migrations + 0019 applied, reading the real contextual store (delicate-moon-28755675).Action for merge: apply migrations 0002 (+0003 roux axes, 0017, 0018) and 0019 to production before enabling the cron phase.
A second gate: the
chittyagent-tasksprod token and local DB-connection injection are both credential-gated (POLICY_BLOCKED_CHITTYCONNECT_UNAVAILABLE; neonctl unauthed). So end-to-end verification used MCPrun_sqlas a SQL-faithful replay of the module's exact write path against the branch — real rows, real schema, real contextual reads; the inline AI branch (Workers-runtime only) did not execute, so the verified rows came via the deterministic fallback classifier (recorded asclassifier_via=deterministic_fallback,confidence = extraction_confidence × 0.7).Verification evidence (real rows on the branch)
before → after:
cc_intents(contextual_ingest)cc_obligations(source=contextual)cc_recommendations(source=contextual)agent_tasks.tasks(reconciliation, chittycommand)payee=Kris amount_due=1000.00 status=candidate source=contextual source_ref=ctx-msg-16884 confidence=0.665 sensitivity=legalink category=legalmanualKris $500; ingest inferred $1000 → raised realagent_tasks.tasksrow6b29b0f4-… reconciliation / chittyagent-command / urgency=today / needs_nick / sensitivity=legalink, payload references the existing obligation. Existing $500 row not overwritten — both coexist.legalinkon intent space + obligation + task; the two payee-null bills (ctx-msg-6398 $1,912.50, ctx-msg-6501 $5,300) →business.Known spec note
The task said triage "picks up candidate obligations," but
runTriage()filtersstatus IN ('pending','overdue')— candidates are intentionally excluded (the emphatic "do NOT write verified/trusted from inference" instruction wins). Promotioncandidate → pendingshould happen on verification by a non-AI source (future follow-up), not at ingest.🤖 Generated with Claude Code
Update (review fixes)
contextual_ingestthroughcreateRouxIngestIntentIdempotent, whoseON CONFLICT/re-fetch are hardcoded tointent_type='roux_ingest'— so contextual intents never deduped (and, with the 0019 index present, the second insert raised a unique violation), causing unbounded goal/plan/intent fan-out per cron tick. Added a dedicatedcreateContextualIngestIntentIdempotentwhose arbiter predicate + re-fetch both filterintent_type='contextual_ingest', targeting the 0019 index. Re-verified on branch: two inserts of a fresh contextual message_id → exactly 1 intent row.fetchContextualCandidatesnow collapses to one candidate per message (DISTINCT ON (message_id), MAX amount), so the deduped obligation is reproducible regardless of row order./process→ inline llama → deterministic fallback) was type-checked but not executed; the verified rows carry values equivalent to the deterministic-fallback path (classifier_via=deterministic_fallback,confidence = extraction_confidence × 0.7 = 0.665). The router and inline-AI branches are execution-unverified (they run only in the Workers runtime / with a reachable router).runTriagefiltersstatus IN ('pending','overdue')and excludescandidateby design (promotion topendinghappens on non-AI verification, a follow-up).