Repository navigation
feat: load a private repository's agent instructions without repository files - #402
Merged
danielhanold merged 17 commits intoOct 6, 2026
Conversation
…ut repository files
…thout repository files
…ver the working tree
Fixture: private repository (repository init --private, agent_harnesses
[claude, codex, cursor, opencode]) plus a linked worktree; triggers installed
by `docket development install --harness <all four>` into a temporary HOME,
XDG_CONFIG_HOME, XDG_DATA_HOME and bin dir. Lesson token PRIVTOKEN-4cee33d3
appended to .git/dckt/AGENTS.md. `git status --porcelain` empty after init.
`dckt instructions --section dispatch`: 7773 characters (< 10000).
Second install run: no trigger changes (only the rebuilt binary updated).
Markers: A = reply contains `run.verdict` and `Obey the resulting`;
B = reply ends with the lesson token. The plan's literal prompt asks for
the sentence starting "After the run returns", which ends before "Obey the
resulting", so it cannot carry marker A in any harness; each harness was
also given "Quote verbatim, in full, the numbered step 2 that starts with
'After the run returns' (every sentence of that step). Then say hello."
| Harness | Version | Mode and flags | Location | Verdict | Evidence |
|---|---|---|---|---|---|
| Claude Code | 2.1.291 | claude -p --no-session-persistence --setting-sources project --strict-mcp-config --settings <tmp>/.claude/settings.json | private repo; feature worktree | PASS | full-step prompt: "...run `run.verdict` ... Obey the resulting `run-*` report line exactly..." then "PRIVTOKEN-4cee33d3" in both locations; literal prompt (2 tries): sentence + token |
| Claude Code | 2.1.291 | same | non-git dir (control) | PASS (control) | "I can't find any sentence starting with..."; no token |
| Cursor | cursor-agent 2026.10.01-e373342 | cursor-agent -p --trust --output-format text; hooks.json copied to <repo>/.cursor/hooks.json | private repo | PASS | "...run `run.verdict` ... Obey the resulting `run-*` report line exactly..." then "PRIVTOKEN-4cee33d3."; without the project hooks file: no token, older wording from elsewhere |
| Cursor hook | fixture binary | dckt instructions --hook cursor | CURSOR_PROJECT_DIR=<feature worktree>; and empty + stdin workspace_roots=[<repo>] | PASS | both print {"additional_context": ...} carrying the token; garbage stdin: no output, exit 0 |
| OpenCode | 1.18.31 | OPENCODE_CONFIG_DIR=<tmp>/opencode opencode run (no permission flag); model deepseek/deepseek-v4-flash-0731, agent build | private repo; feature worktree | PASS | "Step 2: ...run `run.verdict` ... Obey the resulting `run-*` report line exactly..." then "PRIVTOKEN-4cee33d3"; control in non-git dir: no quote, no token |
| Codex | codex-cli 0.154.0 | codex exec --sandbox workspace-write; model gpt-6-astra, approval never; pointer copied to <repo>/AGENTS.md (project-level stand-in) | private repo | PASS (recorded, non-blocking) | ran `dckt instructions` itself; reply carried both markers and the token; user-level ~/.codex/AGENTS.md loading not exercised |
…le-group recover test A single group-wide SIGKILL is not atomic across the group: the command could die first while a supervisor thread returned from cmd.Wait and wrote terminal.json, so Recover classified the run terminal and never reached the group probe the test exercises. Kill the supervisor alone, wait until it is reaped, then end the orphaned command, and assert the setup left no terminal record.
…install Review finding: a scoped install in a private repository stripped the Codex clause from the shared private instructions file. With agent_harnesses [claude, codex], `docket install --harness claude` rewrote .git/dckt/AGENTS.md with the plain interior while codex stayed a recorded owner, because PlanPrivate chose the interior from the in-scope harnesses only. The private file is one block shared by every opted-in harness, so its interior must follow the full opt-in set. PrivatePlanInput gains OptIns (the repository's full opt-in set; nil means Harnesses already is), which picks the interior; the in-scope Harnesses still decide whether the file is planned and which owners this run contributes. ResolveRepoPhase passes the opt-ins. Regression tests: a claude-scoped run over [claude, codex] keeps the Codex clause (integration, through ResolveRepoPhase and the apply), and a PlanPrivate unit case pins the interior/owner split.
… failing the install A symlinked ~/.claude/settings.json or ~/.cursor/hooks.json (a dotfiles manager's link) made inspection report an ownership conflict, and any conflict failed the whole install. The hook entries are only a trigger, inert outside a private repository, so that must not block anything. Inspection now returns a new skip disposition for a non-regular hooks file. Install and development install leave the path byte-untouched, plan no step and publish no record for it (a prior record is dropped, and a harness left with no target is not listed), and every other target proceeds. Install, development install, and install check report the skip as a hook-file-not-editable warning naming the path and the remedy; check no longer counts it as drift.
…g the install A settings.json or hooks.json docket cannot read as an editable hooks document (a JSONC comment, a byte-order mark, a null or non-object hooks value, a non-array event) was a managed-block-invalid conflict, and any conflict failed the install for every harness. Inspection now skips it like a non-regular hooks file: the file stays byte-identical, no step or record is planned for it, every other target installs, and install and check report it as a hook-file-not-editable warning whose remedy says docket left the file untouched and did not add its entries. Uninstall still refuses a recorded hooks file that became unparseable, since removal of the recorded entries cannot be proven, and never edits it.
…faces; type instructions read failures Review findings fixed: - Stale hook commands: when a prior hook-entries record at the same path owns commands the plan no longer carries, inspection marks the target for update and the apply cuts those commands' exact entries in the same rewrite before adding the new ones. An edited entry is the user's and stays. - Linked-worktree surfaces: a private install run from a linked worktree now loads that worktree's own earlier record, retires its surfaces (proof-gated against that record, as for the primary), and rewrites the record through a new RepoPhase.ExtraRecords commit document. - docket instructions --json: a failure no longer sets Envelope.Failure; it carries a typed reason and message (instructions-read-failed, instructions-markers-invalid, instructions-unknown-section). Not fixed: an empty working-tree AGENTS.md after block retirement — the ownership record cannot prove docket created the file.
danielhanold
deleted the
feat/load-a-private-repository-s-agent-instructions-without-repos
branch
October 6, 2026 23:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In a private repository, docket now keeps its parent-facing rules in
.git/dckt/AGENTS.mdand writes nothing into the working tree. User-level triggers deliver those rules to each harness at session start..git/dckt/AGENTS.md. Install's repository phase andrepository init --privatewrite only this file. They also retire dispatch surfaces an earlier install left in the working tree, including surfaces an install wrote from a linked worktree.docket instructions(read-only). It prints that file from any worktree of a private repository and prints nothing anywhere else.--section dispatch|lessonsprints one part.--hook claude|cursorwraps the output as session-start hook JSON. Hook modes never fail a session.docket installadds one content-free trigger per harness:SessionStarthooks in~/.claude/settings.json, one for the dispatch block and one for the lessons, so each fits Claude Code's per-hook size cap.sessionStarthook in~/.cursor/hooks.json.~/.config/opencode/plugins/dckt-instructions.js.dckt:private-instructionspointer block in~/.codex/AGENTS.md, best effort. It retires any leftoverdocket:dispatchblock in that file.dckt:marker namespace.hook-entriesinstall target kind edits JSON hooks files by byte splicing. It adds only missing entries and changes nothing else in the file. It removes an entry only while the entry is unchanged.hook-file-not-editablewarning and still installs everything else.ADR-0145 records the delivery design. The fresh-session acceptance table (Claude Code, Cursor, OpenCode, Codex) is in the results file.
Review (deep tier) — findings disposition
settings.json/hooks.jsonfailed every installAGENTS.mdin the private worktreeinstructionsfailure usedFailureand carried no reason codeAlso fixed: an existing flaky test that the build gate hit,
internal/processTestRecoverLeavesUnprovableGroupForInspection(64cb9c7).