Skip to content

feat: index direct task and steering instructions - #154

Open
andrewesweet wants to merge 24 commits into
kunchenguid:mainfrom
andrewesweet:fm/bp-provenance-widen
Open

andrewesweet wants to merge 24 commits into
kunchenguid:mainfrom
andrewesweet:fm/bp-provenance-widen

Conversation

@andrewesweet

@andrewesweet andrewesweet commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Intent

Backpass indexes only project memory files as instruction sources, yet in measured sessions nearly half of the instructions that governed agent behaviour came from the initial task message or later user steering, which Backpass shows only as undifferentiated transcript evidence. This change makes direct task and steering instructions addressable as instruction sources with stable IDs and source, turn, authority, and lifetime metadata, referencing transcript text already sent rather than duplicating it, while keeping quoted transcripts, tool results, and assistant restatements as evidence rather than authority.

What Changed

  • Make task (TASK-1) and steering (STEER-<turn>) instructions citable during analysis, with source kind, turn, authority, and lifetime metadata referencing existing transcript text.
  • Exclude fenced blocks, quoted lines, assistant messages, and tool results from instruction authority; flag clamped or elided turns for raw-transcript lookup.
  • Keep directive citations separate from memory-instruction scores, retain gaps covered only by session directives, and invalidate older analysis evidence.

Risk Assessment

✅ Low: Changes are bounded, satisfy the amended intent, and introduce no substantiated material defects.

Testing

Focused directive tests and eight live CLI scenarios passed, including real model analysis, cached reuse, and evidence folding. Serial retry resolved initial backend locking; text/JSON evidence was captured and disposable data removed.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Analyze Claude and Pi sessions: task and steering receive matching IDs, turn references, authority, and lifetimes without duplicated text. ✅ pass live Emitted prompt contracts: basic and pi-basic
Submit quoted, fenced, assistant, and tool content: none becomes directive authority; marker-prefixed false closers keep pasted text withheld. ✅ pass live Emitted prompt contracts: adversarial and basic
Use single-marker list fences, mixed-tab closers, and inline backticks: genuine instructions remain addressable. ✅ pass live Emitted prompt contracts: list
Clamp away a pasted block’s closing fence: the following instruction retains TASK-1 and receives a clamped-text notice. ✅ pass live Emitted prompt contracts: lost-fence
Clamp away the only instruction: its index entry requests the raw transcript instead of claiming visible trace text. ✅ pass live Emitted prompt contracts: hidden-instruction
Analyze a capped session: omitted middle directives retain IDs with elision notices while visible endpoints retain trace references. ✅ pass live Emitted prompt contracts: capped
Complete real model analysis: persist a STEER-5 violation, its gap, metadata-only spans, and reuse that evidence without another model call. ✅ pass live Real model analysis and cached reuse; Persisted model evidence
Consume persisted evidence through the CLI: exclude direct citations from memory scores, preserve a corroborated gap, and reject stale v8 records. ✅ pass live Folded evidence and freshness observations
Evidence: Emitted prompt contracts
# Live CLI prompt contracts

The real `backpass scan --json` discovered seven synthetic sessions in an isolated home. The real `backpass analyze --json` emitted the model-input artifacts below. These checks establish prompt generation only; backend completion is reported separately.

Oracle: the author’s direct-task/steering authority contract and recorded fence/truncation decisions. Expected IDs and turns are worked examples from the synthetic sessions, not implementation-derived values.

## adversarial

Actual emitted directive index:

`` `text
[TASK-1] task · turn 24 · authority direct-task · lifetime turns 24+ - see turn 24 in the trace
[STEER-25] steering · turn 25 · authority direct-steering · lifetime turns 25+ - see turn 25 in the trace
`` `

## basic

Actual emitted directive index:

`` `text
[TASK-1] task · turn 3 · authority direct-task · lifetime turns 3+ - see turn 3 in the trace
[STEER-5] steering · turn 5 · authority direct-steering · lifetime turns 5+ - see turn 5 in the trace
[STEER-7] steering · turn 7 · authority direct-steering · lifetime turns 7+ - see turn 7 in the trace
`` `

## capped

Actual emitted directive index:

`` `text
[TASK-1] task · turn 1 · authority direct-task · lifetime turns 1+ - see turn 1 in the trace
[STEER-3] steering · turn 3 · authority direct-steering · lifetime turns 3+ - see turn 3 in the trace
[STEER-5] steering · turn 5 · authority direct-steering · lifetime turns 5+ - see turn 5 in the trace
[STEER-7] steering · turn 7 · authority direct-steering · lifetime turns 7+ - see turn 7 in the trace
[STEER-9] steering · turn 9 · authority direct-steering · lifetime turns 9+ - turn 9 - elided from the trace, open the raw transcript for its text
[STEER-11] steering · turn 11 · authority direct-steering · lifetime turns 11+ - turn 11 - elided from the trace, open the raw transcript for its text
[STEER-13] steering · turn 13 · authority direct-steering · lifetime turns 13+ - turn 13 - elided from the trace, open the raw transcript for its text
[STEER-15] steering · turn 15 · authority direct-steering · lifetime turns 15+ - turn 15 - elided from the trace, open the raw transcript for its text
[STEER-17] steering · turn 17 · authority direct-steering · lifetime turns 17+ - turn 17 - elided from the trace, open the raw transcript for its text
[STEER-19] steering · turn 19 · authority direct-steering · lifetime turns 19+ - turn 19 - elided from the trace, open the raw transcript for its text
[STEER-21] steering · turn 21 · authority direct-steering · lifetime turns 21+ - turn 21 - elided from the trace, open the raw transcript for its text
[STEER-23] steering · turn 23 · authority direct-steering · lifetime turns 23+ - turn 23 - elided from the trace, open the raw transcript for its text
[STEER-25] steering · turn 25 · authority direct-steering · lifetime turns 25+ - turn 25 - elided from the trace, open the raw transcript for its text
[STEER-27] steering · turn 27 · authority direct-steering · lifetime turns 27+ - turn 27 - elided from the trace, open the raw transcript for its text
[STEER-29] steering · turn 29 · authority direct-steering · lifetime turns 29+ - turn 29 - elided from the trace, open the raw transcript for its text
[STEER-31] steering · turn 31 · authority direct-steering · lifetime turns 31+ - turn 31 - elided from the trace, open the raw transcript for its text
[STEER-33] steering · turn 33 · authority direct-steering · lifetime turns 33+ - turn 33 - elided from the trace, open the raw transcript for its text
[STEER-35] steering · turn 35 · authority direct-steering · lifetime turns 35+ - turn 35 - elided from the trace, open the raw transcript for its text
[STEER-37] steering · turn 37 · authority direct-steering · lifetime turns 37+ - turn 37 - elided from the trace, open the raw transcript for its text
[STEER-39] steering · turn 39 · authority direct-steering · lifetime turns 39+ - turn 39 - elided from the trace, open the raw transcript for its text
[STEER-41] steering · turn 41 · authority direct-steering · lifetime turns 41+ - turn 41 - elided from the trace, open the raw transcript for its text
[STEER-43] steering · turn 43 · authority direct-steering · lifetime turns 43+ - turn 43 - elided from the trace, open the raw transcript for its text
[STEER-45] steering · turn 45 · authority direct-steering · lifetime turns 45+ - turn 45 - elided from the trace, open the raw transcript for its text
[STEER-47] steering · turn 47 · authority direct-steering · lifetime turns 47+ - turn 47 - elided from the trace, open the raw transcript for its text
[STEER-49] steering · turn 49 · authority direct-steering · lifetime turns 49+ - turn 49 - elided from the trace, open the raw transcript for its text
[STEER-51] steering · turn 51 · authority direct-steering · lifetime turns 51+ - turn 51 - elided from the trace, open the raw transcript for its text
[STEER-53] steering · turn 53 · authority direct-steering · lifetime turns 53+ - turn 53 - elided from the trace, open the raw transcript for its text
[STEER-55] steering · turn 55 · authority direct-steering · lifetime turns 55+ - see turn 55 in the trace
[STEER-57] steering · turn 57 · authority direct-steering · lifetime turns 57+ - see turn 57 in the trace
[STEER-59] steering · turn 59 · authority direct-steering · lifetime turns 59+ - see turn 59 in the trace
[STEER-61] steering · turn 61 · authority direct-steering · lifetime turns 61+ - see turn 61 in the trace
[STEER-62] steering · turn 62 · authority direct-steering · lifetime turns 62+ - see turn 62 in the trace
`` `

## hidden-instruction

Actual emitted directive index:

`` `text
[TASK-1] task · turn 1 · authority direct-task · lifetime turns 1+ - turn 1 - clamped from the trace, open the raw transcript for its text
`` `

## list

Actual emitted directive index:

`` `text
[TASK-1] task · turn 1 · authority direct-task · lifetime turns 1+ - see turn 1 in the trace
[STEER-2] steering · turn 2 · authority direct-steering · lifetime turns 2+ - see turn 2 in the trace
[STEER-3] steering · turn 3 · authority direct-steering · lifetime turns 3+ - see turn 3 in the trace
[STEER-4] steering · turn 4 · authority direct-steering · lifetime turns 4+ - see turn 4 in the trace
`` `

## lost-fence

Actual emitted directive index:

`` `text
[TASK-1] task · turn 1 · authority direct-task · lifetime turns 1+ - turn 1 - clamped from the trace, open the raw transcript for its text
`` `

## pi-basic

Actual emitted directive index:

`` `text
[TASK-1] task · turn 3 · authority direct-task · lifetime turns 3+ - see turn 3 in the trace
[STEER-5] steering · turn 5 · authority direct-steering · lifetime turns 5+ - see turn 5 in the trace
[STEER-7] steering · turn 7 · authority direct-steering · lifetime turns 7+ - see turn 7 in the trace
`` `

For basic and pi-basic, the task text occurs once in the entire prompt and zero times in the directive index. Assistant restatements, tool responses, and the initial quoted/pasted user turn receive no ID. The final two-word `ship it` receives STEER-7.

For adversarial, all 23 paste-only turns remain evidence-only: bullet and ordered markers cannot close either fence type, mixed-tab openers do not expose pasted text, and listed blockquotes issue no IDs. The real task is turn 24.

For list, single-marker openers and mixed-tab whitespace-only closers preserve all four real directives, including a line containing inline triple-backtick code.

For lost-fence, the closing fence falls outside message-clamp retention but TASK-1 remains indexed, with the retained final instruction in the trace. For hidden-instruction, its only direct instruction is absent from the emitted trace; the index explicitly requests the raw transcript.

For capped, TASK-1 and STEER-62 remain visible. STEER-33 survives as an ID but its text is absent, and the index marks it elided.
Evidence: Real model analysis and cached reuse
{
  "backend": "real acpx with installed opencode acp, provider opencode, model big-pickle; no adapter stub",
  "persistedDirectives": [
    {
      "id": "TASK-1",
      "kind": "task",
      "turn": 3,
      "authority": "direct-task",
      "lifetime": {
        "fromTurn": 3,
        "toTurn": null
      }
    },
    {
      "id": "STEER-5",
      "kind": "steering",
      "turn": 5,
      "authority": "direct-steering",
      "lifetime": {
        "fromTurn": 5,
        "toTurn": null
      }
    },
    {
      "id": "STEER-7",
      "kind": "steering",
      "turn": 7,
      "authority": "direct-steering",
      "lifetime": {
        "fromTurn": 7,
        "toTurn": null
      }
    }
  ],
  "acceptedNegatives": [
    {
      "instruction": "STEER-5",
      "moment": "turn 6",
      "effect": "Ignored directive to keep old refresh-token flow for mobile clients; removed it causing mobile clients to be unable to authenticate.",
      "quote": "I removed the old refresh-token flow; mobile clients cannot authenticate.",
      "class": "non-compliance"
    }
  ],
  "gaps": [
    {
      "mistake": "Misread 'Delete all tests' as intent to delete everything and committed to deleting everything.",
      "proposedInstruction": "Confirm the exact scope of destructive commands before executing them.",
      "recurrenceRisk": "high",
      "quote": "I will delete everything.",
      "domain": "project"
    },
    {
      "mistake": "Ignored explicit user instruction to preserve the old refresh-token flow for mobile clients.",
      "proposedInstruction": "Follow explicit preservation instructions for compatibility paths unless the user explicitly overrides them.",
      "recurrenceRisk": "high",
      "quote": "I removed the old refresh-token flow; mobile clients cannot authenticate.",
      "domain": "project"
    }
  ],
  "cachedRun": {
    "total": 1,
    "cached": 1,
    "analyzed": 0,
    "skipped": 0,
    "failed": 0,
    "usage": [],
    "staleMemoryHash": 0,
    "quotesNotInTrace": 0
  }
}
Evidence: Persisted model evidence
{
  "transcript": {
    "harness": "claude",
    "id": "claude-basic",
    "identity": "38508c62cc4f775754cc809ff45faa332da5286ea89f34cb89fc3a811c963fc1",
    "parentSessionId": null,
    "corroborationIdentity": "38508c62cc4f775754cc809ff45faa332da5286ea89f34cb89fc3a811c963fc1",
    "corroborationNativeId": "basic",
    "corroborationStartedAt": 1792605880090,
    "path": "~/.no-mistakes/worktrees/a682c286f628/01M5FSWJ3Y1WW68YBGSBTEM2WF/.gate-live/home/.claude/projects/live/basic.jsonl",
    "mtimeMs": 1792605944054,
    "bytes": 2847,
    "startedAt": 1792605880090,
    "association": {
      "tier": 1,
      "confidence": "exact",
      "reason": "cwd is worktree ~/.no-mistakes/worktrees/a682c286f628/01M5FSWJ3Y1WW68YBGSBTEM2WF/.gate-live/repo",
      "project": "~/.no-mistakes/worktrees/a682c286f628/01M5FSWJ3Y1WW68YBGSBTEM2WF/.gate-live/repo",
      "projectRoot": "~/.no-mistakes/worktrees/a682c286f628/01M5FSWJ3Y1WW68YBGSBTEM2WF/.gate-live/repo"
    },
    "interaction": "non-interactive",
    "cwd": "~/.no-mistakes/worktrees/a682c286f628/01M5FSWJ3Y1WW68YBGSBTEM2WF/.gate-live/repo",
    "project": "~/.no-mistakes/worktrees/a682c286f628/01M5FSWJ3Y1WW68YBGSBTEM2WF/.gate-live/repo",
    "projectRoot": "~/.no-mistakes/worktrees/a682c286f628/01M5FSWJ3Y1WW68YBGSBTEM2WF/.gate-live/repo",
    "host": null
  },
  "memoryHash": "sha256:593ffe7f85f4324e",
  "memoryPath": "MEMORY.md",
  "key": "38508c62cc4f775754cc809ff45faa332da5286ea89f34cb89fc3a811c963fc1:1792605944054:2847:sha256:593ffe7f85f4324e:analysis-index-v9",
  "analyzedAt": "2026-10-21T18:05:45.444Z",
  "status": "ok",
  "stats": {
    "userTurns": 4,
    "assistantTurns": 3,
    "toolCalls": 1,
    "elided": false,
    "distilledTokens": 269
  },
  "positive": [],
  "negative": [
    {
      "instruction": "STEER-5",
      "moment": "turn 6",
      "effect": "Ignored directive to keep old refresh-token flow for mobile clients; removed it causing mobile clients to be unable to authenticate.",
      "quote": "I removed the old refresh-token flow; mobile clients cannot authenticate.",
      "class": "non-compliance"
    }
  ],
  "gaps": [
    {
      "mistake": "Misread 'Delete all tests' as intent to delete everything and committed to deleting everything.",
      "proposedInstruction": "Confirm the exact scope of destructive commands before executing them.",
      "recurrenceRisk": "high",
      "quote": "I will delete everything.",
      "domain": "project"
    },
    {
      "mistake": "Ignored explicit user instruction to preserve the old refresh-token flow for mobile clients.",
      "proposedInstruction": "Follow explicit preservation instructions for compatibility paths unless the user explicitly overrides them.",
      "recurrenceRisk": "high",
      "quote": "I removed the old refresh-token flow; mobile clients cannot authenticate.",
      "domain": "project"
    }
  ],
  "usedRawTranscript": false,
  "quotesNotInTrace": 0,
  "directives": [
    {
      "id": "TASK-1",
      "kind": "task",
      "turn": 3,
      "authority": "direct-task",
      "lifetime": {
        "fromTurn": 3,
        "toTurn": null
      }
    },
    {
      "id": "STEER-5",
      "kind": "steering",
      "turn": 5,
      "authority": "direct-steering",
      "lifetime": {
        "fromTurn": 5,
        "toTurn": null
      }
    },
    {
      "id": "STEER-7",
      "kind": "steering",
      "turn": 7,
      "authority": "direct-steering",
      "lifetime": {
        "fromTurn": 7,
        "toTurn": null
      }
    }
  ]
}
Evidence: Folded evidence and freshness observations
{
  "input": "two synthetic persisted evidence records with TASK-1, STEER-3, AG-001 and the same direct-only gap",
  "staleAnalyzedSessions": 0,
  "freshAnalyzedSessions": 2,
  "instructionRows": [
    {
      "instruction": "AG-001",
      "positive": 2,
      "negative": 0
    }
  ],
  "gap": {
    "proposedInstruction": "Preserve the legacy refresh-token flow for mobile clients when migrating authentication.",
    "sessions": 2,
    "projects": 1,
    "projectCoveredSessions": 0,
    "recurrenceRisk": "high",
    "quotes": [
      {
        "text": "I removed the old refresh-token flow; mobile clients cannot authenticate.",
        "effect": "Dropped the refresh-token flow the user asked to keep",
        "source": "claude · fold-b · 2026-10-21"
      },
      {
        "text": "I removed the old refresh-token flow; mobile clients cannot authenticate.",
        "effect": "Dropped the refresh-token flow the user asked to keep",
        "source": "claude · fold-a · 2026-10-21"
      }
    ],
    "orchestrationSightings": 0,
    "mixed": false,
    "majorityOrchestration": false,
    "id": "d9e7e0bfba1411b0",
    "ids": [
      "d9e7e0bfba1411b0"
    ]
  },
  "scope": "real CLI fold and freshness consumption; no model-generated record or synthesis claim"
}
Evidence: Validation scope and reproduction commands
{
  "result": "All eight targeted live scenarios passed; no project edits were required.",
  "commands": [
    "node --import ./test/helpers/hermetic-config.js --test test/directives.test.js",
    "node .gate-live/setup.mjs",
    "node .gate-live/run.mjs scan analyze",
    "node .gate-live/check-prompts.mjs",
    "node .gate-live/retry.mjs",
    "node .gate-live/check-fold.mjs",
    "node .gate-live/check-analysis.mjs"
  ],
  "setup": "A nested disposable git repository, MEMORY.md, separate HOME/XDG/cache/temp directories, six Claude sessions, one Pi session, and a separate two-session persisted-evidence corpus. All inputs were synthetic; the child environment excluded operator configuration and credentials.",
  "backend": "Initial parallel OpenCode startup encountered SQLite locking and timeouts. Retrying jobs=1 with a 60-second prompt timeout completed real model-backed analysis using the installed opencode acp adapter and the public opencode/big-pickle model. Repeating analysis reused the evidence with no model call.",
  "promptScope": "All seven exact model-input prompts were emitted by the real CLI and verified as generated interfaces. Prompt-only scenarios claim generation and reference semantics, not model interpretation of every transcript.",
  "foldScope": "backpass propose consumed two synthetic persisted records, wrote its real folded summary, and stopped at the deliberately unavailable synthesis agent. The scenario validates folding and freshness, not synthesis. v8 records yielded zero admitted sessions; v9 records yielded two, only AG-001 memory scoring, and one recurring direct-only gap.",
  "supplemental": "Focused executable tests cover fabricated directive IDs and quotes in addition to fence, metadata, truncation, and fold regressions. These unit tests are not counted as live scenarios.",
  "visualEvidence": "No rendered UI changed or was needed: the changed surface is generated analysis prompts and CLI/persisted evidence. Exact text/JSON artifacts provide the observable contracts.",
  "cleanup": "All disposable working-tree files and generated runtime data are removed after evidence capture; no source, memory, or test files changed."
}

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Analyze Claude and Pi sessions: task and steering receive matching IDs, turn references, authority, and lifetimes without duplicated text. ✅ pass live Emitted prompt contracts: basic and pi-basic
Submit quoted, fenced, assistant, and tool content: none becomes directive authority; marker-prefixed false closers keep pasted text withheld. ✅ pass live Emitted prompt contracts: adversarial and basic
Use single-marker list fences, mixed-tab closers, and inline backticks: genuine instructions remain addressable. ✅ pass live Emitted prompt contracts: list
Clamp away a pasted block’s closing fence: the following instruction retains TASK-1 and receives a clamped-text notice. ✅ pass live Emitted prompt contracts: lost-fence
Clamp away the only instruction: its index entry requests the raw transcript instead of claiming visible trace text. ✅ pass live Emitted prompt contracts: hidden-instruction
Analyze a capped session: omitted middle directives retain IDs with elision notices while visible endpoints retain trace references. ✅ pass live Emitted prompt contracts: capped
Complete real model analysis: persist a STEER-5 violation, its gap, metadata-only spans, and reuse that evidence without another model call. ✅ pass live Real model analysis and cached reuse; Persisted model evidence
Consume persisted evidence through the CLI: exclude direct citations from memory scores, preserve a corroborated gap, and reject stale v8 records. ✅ pass live Folded evidence and freshness observations
  • node --import ./test/helpers/hermetic-config.js --test test/directives.test.js
  • node .gate-live/setup.mjs — created isolated synthetic transcript stores and disposable repositories.
  • node .gate-live/run.mjs scan analyze — exercised real CLI discovery and prompt emission.
  • node .gate-live/check-prompts.mjs — verified emitted prompt contracts.
  • node .gate-live/retry.mjs — completed serial analysis through real OpenCode using opencode/big-pickle.
  • node .gate-live/check-fold.mjs — exercised CLI folding with current and stale persisted evidence.
  • node .gate-live/check-analysis.mjs — verified model-produced citations, metadata, gaps, and cached reuse.
  • Removed disposable runtime data and confirmed a clean working tree.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The latest change appears safe to merge; the two earlier suggestions remain non-blocking.

Reviews (10) · Last reviewed commit: "no-mistakes(ci): Fixed signal-test start..." · Reviewed by Greptile

Comment thread src/directives.js Outdated
Comment thread src/directives.js Outdated
export const TASK_AUTHORITY = "direct-task";
export const STEERING_AUTHORITY = "direct-steering";

const FENCE = /^\s*(`{3,}|~{3,})([^\n]*)$/;

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 Indented code changes authority This matcher accepts fence-like lines indented four or more spaces, even though those lines belong to an indented code block. In a pasted sample, they can close a fence early or open one that should not exist. That can make pasted text appear to be a user instruction, or hide a real instruction that follows.

Suggested change
const FENCE = /^\s*(`{3,}|~{3,})([^\n]*)$/;
const FENCE = /^ {0,3}(`{3,}|~{3,})([^\n]*)$/;

Comment thread src/directives.js Outdated
@andrewesweet andrewesweet changed the title feat(analyze): index direct task and steering instructions as addressable sources feat(analyze): index direct task and steering instructions as citable sources Sep 25, 2026
Comment thread src/distill.js
@greptile-apps

greptile-apps Bot commented Sep 25, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: reviewed this PR against main de6a9aa (release 0.1.27) and VISION.md.

Diff verdict: safe for the intended scope. Adds always-on TASK-1 / STEER-N indexing of user-turn envelopes into analysis (new src/directives.js), bumps ANALYSIS_INDEX_VERSION 3→4, teaches sanitize/fold that directive cites are session authority (never durable memory rows), and indexes by turn reference only (or elided/clamped). Envelope text does not persist. No write-path / memory-scope widen. No security concern. Does not close unrelated issues.

VISION per-rule:

  • Purpose / owns the backward pass — aligns (stronger addressable evidence for what steered a session; still the same backward pass).
  • Evidence is the only currency — aligns (quotes still required; fold skips directive ids so they cannot launder into memory-instruction rows; gap sightings still carry recurrence).
  • The human owns the weights — aligns (analysis/index only; no auto-write; session authority, not durable project memory).
  • Nothing the model says is taken on faith — aligns (index by measured turns/elision/clamp, not model arithmetic).
  • The budget is the constraint — aligns (reference-only index; no duplicating turn text into the prompt).
  • It reads what you already have and owns nothing — aligns (local transcripts; no new upload/key/server).
  • Failure is loud and named — aligns (elided/clamped turns named in the index; tests pin clamp/indent/elision cases).
  • Scope — aligns (analysis evidence quality, not a write/scope widen).
  • Closing resist/align lines — aligns on evidence strength; product default changes for every unconfigured analyze run.

contract-class: new-default (independent of contributor claim) — main's unconfigured analysis path has no directive index and ANALYSIS_INDEX_VERSION=3; this always-on indexes TASK/STEER and invalidates the analysis cache. Not restore; not opt-in.

CI: fork workflows approved (attempt 2). All required checks green (CI ubuntu+macos, Guard, no-mistakes). Greptile ignored. mergeable/ready on checks.

Hold: this is waiting on captain, not on the author. Auto-merge is blocked solely by the new-default decision. Flagging for Firstmate/captain merge-or-not.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: stale-restamp pass.

Thanks for your patience on this one, @andrewesweet. The head is still b45d83d, but main has moved to 0268201 (release 0.1.32), and GitHub now reports this PR as conflicting (DIRTY). Could you rebase it onto current main through git push no-mistakes, so the attestation and CI run against the new head? While you're there, please post a short live analysis run showing the TASK-1 / STEER-N entries in a real session's index.

The earlier per-rule VISION verdict still stands: everything aligns. The contract-class is unchanged: new-default, because every unconfigured analyze run gets the always-on directive index and the ANALYSIS_INDEX_VERSION bump invalidates the analysis cache. Once it's rebased and green, it goes back to Kun for the merge-or-not decision on that default change.

@andrewesweet andrewesweet changed the title feat(analyze): index direct task and steering instructions as citable sources feat: make task and steering instructions citable in analysis Oct 9, 2026
@andrewesweet

Copy link
Copy Markdown
Contributor Author

The branch was updated by merging current main (release 0.1.32) into it as a true
merge commit. Nothing was rebased and nothing was force-pushed, so review history
stays intact. The PR now shows mergeable.

The remaining Greptile P2 suggestion ("Indented code changes authority") is
covered by the relative fence-indent bound already in the branch: a fence may
open at any indentation (so list-item code blocks still count as pastes), but
only a closer indented at most three columns past its opener closes the block,
so fence-like lines inside an indented code block can neither close a paste
early nor open one that should not exist. This commit adds the missing boundary
test: a closer three columns past the opener closes, four past it does not. The
pipeline's review round additionally corrected tab-stop handling in the indent
helper and strengthened a clamp fixture; those commits are in the head.

Live evidence for the directive index, built without a model call by reading one
local pi session from backpass development work through the pi adapter and running
distill → extractDirectives → renderDirectiveIndex:

[TASK-1] task · turn 1 · authority direct-task · lifetime turns 1+ - turn 1 - clamped from the trace, open the raw transcript for its text
[STEER-3] steering · turn 3 · authority direct-steering · lifetime turns 3+ - see turn 3 in the trace
[STEER-5] steering · turn 5 · authority direct-steering · lifetime turns 5+ - see turn 5 in the trace
[STEER-7] steering · turn 7 · authority direct-steering · lifetime turns 7+ - see turn 7 in the trace
[STEER-9] steering · turn 9 · authority direct-steering · lifetime turns 9+ - see turn 9 in the trace
[STEER-11] steering · turn 11 · authority direct-steering · lifetime turns 11+ - see turn 11 in the trace
[STEER-13] steering · turn 13 · authority direct-steering · lifetime turns 13+ - see turn 13 in the trace
[STEER-15] steering · turn 15 · authority direct-steering · lifetime turns 15+ - see turn 15 in the trace
[STEER-17] steering · turn 17 · authority direct-steering · lifetime turns 17+ - see turn 17 in the trace

The session has 9 user turns and was not elided; its long first turn was clamped
out of the trace, so TASK-1 carries the clamped marker instead of a trace
pointer. Only ids, turn references, and lifetimes are shown; no turn text is
duplicated.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: thanks @andrewesweet, both blockers from the last pass are cleared.

  • Merge conflict: resolved by merging current main (0268201, release 0.1.32). Head is now d0f38d0, and GitHub reports it CLEAN.
  • Live evidence: the pi-session directive index you posted shows TASK-1 / STEER-N entries with the clamped marker on the long first turn and no turn text duplicated. That covers what I asked for.
  • CI: every required check is green on d0f38d0: no-mistakes, the generated-files guard, and build-and-test on ubuntu and macOS.

I reviewed the commits added since b45d83d (the fence boundary test, the tab-stop fix in indentColumns, the clamp fixture, docs, and the ANALYSIS_INDEX_VERSION bump from 4 to 5). They stay in the same 10 files as before. No workflow, write-path, or security changes.

VISION per-rule (re-checked against the current VISION.md and this head):

  • Purpose / one backward pass: aligns. Gives analysis addressable evidence for what steered a session. The product is still the same transcript-to-proposal pass.
  • Evidence is the only currency: aligns. Directive cites still need verbatim quotes (invented IDs and fabricated quotes are rejected). Fold excludes them from memory-instruction scoring, and the two-session bar is untouched.
  • The human owns the weights: aligns. Analysis/index only. No new write path, and per-edit review is unchanged.
  • Nothing the model says is taken on faith: aligns. Turn IDs, elision, and clamping come from measured distillation, not from the model.
  • The budget is the constraint: aligns. The index is reference-only and doesn't repeat turn text in the prompt.
  • Reads what you already have, owns nothing: aligns. Local transcripts only. No key, server, or upload.
  • Failure is loud and named: aligns. Elided and clamped turns are named and point at the raw transcript. Regression tests pin the fence, tab, and clamp cases.
  • Scope: aligns. It improves analysis evidence quality and doesn't widen write scope.
  • Closing align/resist lines: aligns (stronger, harder-to-fabricate evidence). Nothing on the resist list.

contract-class: new-default. Main's unconfigured analyze path has no directive index. This head always builds the index and bumps ANALYSIS_INDEX_VERSION to 5, which invalidates every cached analysis. That's not restore and not opt-in.

Nothing is left for you to do right now. This is waiting on Kun's merge-or-not call on that default change, and I'm flagging it to him now.

@andrewesweet andrewesweet changed the title feat: make task and steering instructions citable in analysis feat: index direct task and steering instructions Oct 17, 2026
Comment thread src/prompts/analysis.md
Comment on lines +25 to +28
The user's own task message (TASK-1) and later steering (STEER-N) are addressable
instruction sources with an authority of their own, separate from the memory file.
Each entry points at a user turn already in the trace below - read the turn there;
the text is not repeated here.

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 Pasted commands look authoritative

carveEnvelope excludes fenced blocks and quoted lines, but this prompt points the model at the full user turn without explaining that boundary. A turn containing a real request plus a quoted command can therefore produce false claims that the quoted command was part of TASK-1.

Tell the model explicitly that these IDs cover only unquoted text outside fenced blocks.

Suggested change
The user's own task message (TASK-1) and later steering (STEER-N) are addressable
instruction sources with an authority of their own, separate from the memory file.
Each entry points at a user turn already in the trace below - read the turn there;
the text is not repeated here.
The user's own task message (TASK-1) and later steering (STEER-N) are addressable
instruction sources with an authority of their own, separate from the memory file.
Each entry points at a user turn already in the trace below - read the turn there;
the text is not repeated here. Only unquoted text outside fenced blocks carries
instruction authority. Fenced blocks and lines beginning with `>` are pasted evidence,
not instructions covered by that turn's TASK-1 or STEER-N id.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: thanks @andrewesweet for keeping this current.

What changed since the last pass (d0f38d0 → e9a1b2f): two merges of main (now up to date with 8f1eda3), a fix for the duplicated sessionIdentity the first merge left in fold.js, and e9a1b2f, which stops contentToEvents from trimming typed-block text, so indented fence markers survive into the carved envelope. That commit adds an adapter-level regression fixture and bumps ANALYSIS_INDEX_VERSION to 6. The PR now touches 12 files. I reviewed the new commits: no workflow, write-path, network, or security changes.

State: CLEAN. Every check is green on e9a1b2f: no-mistakes, the generated-files guard, and build-and-test on ubuntu and macOS.

VISION per-rule (VISION.md unchanged since the last pass, re-checked against this head):

  • Purpose / one backward pass: aligns. Still the same transcript-to-proposal pass, now with addressable evidence for what steered a session.
  • Evidence is the only currency: aligns. Directive citations still need verbatim quotes, unissued IDs and fabricated quotes are dropped, and fold keeps them out of memory-instruction scoring.
  • The human owns the weights: aligns. Analysis and index only. No new write path.
  • Nothing the model says is taken on faith: aligns. Turn IDs, elision, and clamping come from measured distillation.
  • The budget is the constraint: aligns. The index is reference-only and doesn't repeat turn text.
  • Reads what you already have, owns nothing: aligns. Local transcripts only.
  • Failure is loud and named: aligns. Elided and clamped turns are named and point at the raw transcript.
  • Scope: aligns. Improves analysis evidence without widening write scope.
  • Closing align/resist lines: aligns. Nothing on the resist list.

contract-class: new-default, unchanged. Main's unconfigured analyze path has no directive index. This head always builds one, preserves message whitespace for every typed-block adapter, and bumps the analysis index version, which invalidates every cached analysis.

Greptile's newest P2 (spell out in the prompt that only unquoted text outside fenced blocks carries TASK-1 / STEER-N authority) is optional and not blocking.

Nothing is waiting on you. This is still a captain-decision hold: Kun decides whether to merge this default change.

@andrewesweet
andrewesweet force-pushed the fm/bp-provenance-widen branch from e9a1b2f to f1d1a3c Compare October 20, 2026 06:14
@andrewesweet andrewesweet changed the title feat: index direct task and steering instructions feat: make direct task and steering instructions citable Oct 20, 2026
Comment thread src/directives.js Outdated
@andrewesweet

Copy link
Copy Markdown
Contributor Author

Rebase-only refresh onto current main validated: the branch was rebased onto upstream main at 3986087 (release 0.1.36) through the no-mistakes pipeline, with the two rebase conflicts in AGENTS.md / src/analyze.js resolved to the branch-side content that already carried the earlier merged upstream changes. All 5 checks are green at f1d1a3c; the PR is mergeable; no behaviour change.

Test evidence note for this run: in the live validation pass, 5 of 6 scenarios were driven live against the product. The model-backed analysis scenario (issued directive citations persist with metadata only, invented IDs are rejected, and updated evidence is reusable) was not driven live in this run — the validation lane's isolated model auth blocked it, and the inconclusive verdict was accepted as-is rather than adding model calls or spend. The live evidence for that scenario remains the pi-session directive index posted earlier in this thread (the TASK-1 / STEER-N entries from the 2026-10-09 pass).

@andrewesweet andrewesweet changed the title feat: make direct task and steering instructions citable feat: make task and steering instructions citable Oct 20, 2026
@andrewesweet

Copy link
Copy Markdown
Contributor Author

Fix-round validation for the list-item-fence finding above: the review step caught that the first cut let marker-prefixed lines close a paste; closing fences now require a whitespace-only prefix, ANALYSIS_INDEX_VERSION moves to 8 so cached evidence under the old acceptance is invalidated, and all 6 checks are green at e332d93.

Test evidence note, same ruling as the previous run: 7 of 8 scenarios were driven live against the product. The authenticated model-backed analysis scenario (issued directive citations persist with metadata only, invented IDs rejected, unsupported quotes rejected) was not driven live in this run — the validation lane's isolated model auth blocks it and the inconclusive verdict was accepted as-is rather than authorising model spend. The live evidence for directive citations remains the pi-session directive index posted earlier in this thread (the TASK-1 / STEER-N entries from the 2026-10-09 pass).

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: thanks @andrewesweet for the rebase and the list-item fence fix.

What changed since the last pass (e9a1b2f → e332d93): the branch is rebased onto 3986087 (main has since added only the 0.1.37 release commit c22f2ad). The new commits fix the list-item fence case Greptile raised: FENCE now accepts an optional bullet or ordered list marker before an opener, closing fences still need a whitespace-only prefix so a marker line inside a paste can't close it, and ANALYSIS_INDEX_VERSION moves to 8 with a test that v7 evidence is treated as stale. The PR now touches 13 files. I reviewed the new commits: no workflow, write-path, network, or security changes.

State: CLEAN. Every check is green on e332d93: no-mistakes, the generated-files guard, and build-and-test on ubuntu and macOS. Noted that the model-backed citation scenario wasn't driven live in this run; the earlier pi-session directive index still stands as the live evidence for that.

VISION per-rule (VISION.md unchanged, re-checked against this head):

  • Purpose / one backward pass: aligns. Same transcript-to-proposal pass, with addressable evidence for what steered a session.
  • Evidence is the only currency: aligns. Directive cites still need verbatim quotes, unissued IDs and fabricated quotes are dropped, and fenced pastes (now including list-item fences) stay out of the instruction envelope.
  • The human owns the weights: aligns. Analysis and index only. No new write path.
  • Nothing the model says is taken on faith: aligns. Turn IDs, elision, and clamping come from measured distillation.
  • The budget is the constraint: aligns. The index is reference-only and doesn't repeat turn text.
  • Reads what you already have, owns nothing: aligns. Local transcripts only.
  • Failure is loud and named: aligns. Elided and clamped turns are named and point at the raw transcript.
  • Scope: aligns. Improves analysis evidence without widening write scope.
  • Closing align/resist lines: aligns. Nothing on the resist list.

contract-class: new-default, unchanged. Main's unconfigured analyze path has no directive index. This head always builds one and bumps the analysis index version, which invalidates every cached analysis.

Nothing is waiting on you. This is still a captain-decision hold: Kun decides whether to merge this default change.

…e spans

The initial task message and later user steering become addressable
instruction units (TASK-1 / STEER-N) with source kind, turn, a
direct-task/direct-steering authority tier, and a lifetime closed only by
an explicit revision signal. Spans render in the analysis prompt by turn
reference, never by duplicating turn text; only the authoritative
envelope of a user message indexes (fenced blocks and blockquote lines
are structurally excluded, assistant/tool text never is). The fold
validates cites against the spans each session issued and renders them
apart from memory-instruction rows. ANALYSIS_INDEX_VERSION 3 -> 4.
…ay "see turn N in the trace" only when that turn's text actually reached the trace. Two ways it cannot: capTrace elision (already handled) and per-message clampMessage, which was unhandled because the envelope is now carved pre-clamp. Changes: - `src/distill.js`: each message turn records `clamped: text !== redacted.trim()` — true exactly when clampMessage dropped a middle from the text the model reads. Whole-turn flag, matching the existing whole-turn `elided` flag (same conservatism as the tail-boundary case). - `src/directives.js`: spans carry `clamped`; `renderDirectiveIndex` renders `turn N - clamped from the trace, open the raw transcript for its text` (after the elided branch, before the pointer branch). Header/JSDoc comments updated. No other consumer touched: `directiveMetadata` still persists only ids/turn/authority/lifetime. - `test/directives.test.js`: new test — a user message over the 6,000-char clamp whose only instruction sits in the clamped middle yields `spans[0].clamped === true`, renders the clamped entry, and never renders "see turn 1 in the trace"; also asserts the trace itself lacks the instruction. ci-4 out of scope per instruction. `pnpm run check` exits 0 (711 tests pass, lint/format/typecheck clean)
The branch already bounds fence indentation relative to the opener, which is
what the indented-code review note asked for while keeping list-item fences
working; an absolute ^ {0,3} form would regress those. Add the missing boundary
test: a closer three columns past the opener closes, four past it does not.
Join typed-block texts without trimming so indented fence markers survive into the carved envelope; whitespace-only blocks stay omitted via the non-empty check. New adapter-level regression test plus fixture. Bump ANALYSIS_INDEX_VERSION 5->6: preserved leading/trailing whitespace changes what analysis accepts.
…nvelope

Greptile P2 on PR 154 (comment 4322029695): carveEnvelope missed fence
openers behind a list marker, so a pasted '- ```sh' block stayed
instruction text and its indented closer opened a phantom fence that
swallowed every later instruction. The opener now matches after an
optional bullet (-, *, +) or ordered marker (digits with . or )) plus
spaces, and the closer indent is measured from the fence's own column
after that marker. Regression tests cover the comment's exact case and
the ordered-list variant. Bump ANALYSIS_INDEX_VERSION to 7 for the
changed envelope boundary.
@andrewesweet
andrewesweet force-pushed the fm/bp-provenance-widen branch from e332d93 to 3293ea0 Compare October 21, 2026 17:44
@andrewesweet andrewesweet changed the title feat: make task and steering instructions citable feat: index direct task and steering instructions Oct 21, 2026
Comment thread src/directives.js
…elope

A blockquote behind a single list marker (- > x, * > x, 1. > x) passed the
quote check and was indexed as instruction text instead of staying evidence,
so a paste-only turn could receive TASK-1. The QUOTE check now allows the
same optional single marker the fence check accepts; markers require
whitespace after them, so an unspaced dash keeps its old behaviour.

Bump ANALYSIS_INDEX_VERSION to 9 so cached analysis accepted under the old
carve is invalidated.
…ess only after installing its SIGTERM handler. Reproduced CI’s exact failure with a forced scheduling delay; same probe passes after fix. All 43 harness/subprocess tests pass locally. Temporary probe removed
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.

2 participants