Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
94aa030
fix(test replay): a sentence-ending root is hidden, no casts, real pa…
ryota-murakami Oct 22, 2026
57a495e
test: API 1.8 title, gated reads and report-check waits, reveal count…
ryota-murakami Oct 22, 2026
ef184c9
fix(test run): a stop before the history or the first stop reads as s…
ryota-murakami Oct 22, 2026
24bed1e
fix(test replay): bounded Vitest version digits, a root Vitest linked…
ryota-murakami Oct 22, 2026
1cc746c
test(test replay): the poll read count is asserted at its boundary, d…
ryota-murakami Oct 22, 2026
3de0642
fix(hook): CRLF-tolerant sections, rule printed when a section is mis…
ryota-murakami Oct 22, 2026
7430235
fix(test replay): the root Vitest read failure is sanitized; ready, r…
ryota-murakami Oct 22, 2026
08a06aa
test(test replay): the missing or escaping Vitest program is refused;…
ryota-murakami Oct 22, 2026
ce7df10
fix(test run): the dirty-editor and Vitest program checks refuse in p…
ryota-murakami Oct 22, 2026
27b87f5
perf(test replay): one concurrent read per cited file; additional-sto…
ryota-murakami Oct 22, 2026
8952d2e
test(removal): wait for the reader's publish after the other study's …
ryota-murakami Oct 22, 2026
226cc9a
test(wiki update): wait for the marker change's answer and give the t…
ryota-murakami Oct 22, 2026
05ec3a1
test(wiki reader): time each crafted page as the shorter of two runs,…
ryota-murakami Oct 22, 2026
7fa975d
test(named pipes): bound only the act of the at-once tests by 10 s, n…
ryota-murakami Oct 22, 2026
8bf7188
test(wiki reader): the manifest's named-pipe test bounds only its rea…
ryota-murakami Oct 22, 2026
83b1e16
feat(skill): 1.0.47 claims saved learning only with an empty captureM…
ryota-murakami Oct 22, 2026
90ee892
docs(readme): Output how-to, recovery rows, node-test fields, capacit…
ryota-murakami Oct 22, 2026
1f129c1
fix(test run): full saved-study capacity makes node-test not ready an…
ryota-murakami Oct 22, 2026
faeefbb
merge: Cycle 0050 PR-1 group C (F28 F38 F60 F74 F80 F81 F84 F86 F94 F…
ryota-murakami Oct 22, 2026
38805a3
fix(test shards): one failed test reads “1 test failed” in a refused …
ryota-murakami Oct 22, 2026
a8c9f98
fix(test replay): the history store scans a growing recording by its …
ryota-murakami Oct 22, 2026
571d1bd
docs(agents): the owner's ship rule of 2026-10-23: after three unconv…
ryota-murakami Oct 22, 2026
92d66ba
fix(test run): sanitized and bounded causes, typed revision conflict,…
ryota-murakami Oct 22, 2026
14dc905
test: value cards with protects, fails_when and no_claim on the exist…
ryota-murakami Oct 22, 2026
c96a238
fix(test replay): Save before the first stop does nothing, receipts f…
ryota-murakami Oct 22, 2026
e8b9205
test(test replay): citation refusals, catalog summary members, resour…
ryota-murakami Oct 22, 2026
0d74ba5
fix(test run): earlier-session replay refusal, release-failed run off…
ryota-murakami Oct 22, 2026
b16ced3
fix(test run): a transient seal failure is retried once after 200 ms …
ryota-murakami Oct 22, 2026
0913678
test(test run): refused-run reason and actions, material saved before…
ryota-murakami Oct 22, 2026
eab8a7f
test(test replay): the navigation fixture's Node history records one …
ryota-murakami Oct 22, 2026
0b82d7a
test(test replay): the pane's open-failure line is the sanitized caus…
ryota-murakami Oct 22, 2026
7cf60e5
fix(test run): late continuation keeps a failed phase, history pages …
ryota-murakami Oct 22, 2026
c792f75
fix(test replay): a legacy history sealed with no entry takes no plac…
ryota-murakami Oct 22, 2026
b047a76
fix(test run): a declared stop bound at another column of its line is…
ryota-murakami Oct 22, 2026
d23456d
fix(test run): the citation-position refusal names the stop, anchor, …
ryota-murakami Oct 22, 2026
8f57cf3
merge: Cycle 0050 PR-1 group A2 (F10 F11 F14 F15 F18 F22 F27 F29 F30 …
ryota-murakami Oct 22, 2026
ebfb2f0
test(test replay): the waits for the partial notice's open and Output…
ryota-murakami Oct 22, 2026
e23b61d
ci(windows): the Windows job starts for the at-once helpers its named…
ryota-murakami Oct 22, 2026
37483f1
merge: Cycle 0050 PR-1 group B (F13 F16 F17 F19 F23 F24 F26 F37 F39 F…
ryota-murakami Oct 22, 2026
191341a
merge: Cycle 0050 PR-1 group A1 (F9 F20 F21 F25 F32 F33 F36 F44 F48 F…
ryota-murakami Oct 22, 2026
691cd53
fix(causes): a root named by its realpath alias sorts by its short sp…
ryota-murakami Oct 22, 2026
47ef72d
test(removal): read the refused removal's line before the run that re…
ryota-murakami Oct 22, 2026
63c672c
docs(agents): the cycle rule names the ship review limit as its one e…
ryota-murakami Oct 22, 2026
4520f05
fix(test replay): Save on a run that ended before its first stop is a…
ryota-murakami Oct 22, 2026
11c0068
docs(readme): reword the Save-before-first-stop sentence (F228)
ryota-murakami Oct 22, 2026
896f281
fix(test replay): one summary read per history at a time, and a trans…
ryota-murakami Oct 22, 2026
37208cf
fix(test replay): a Save receipt returns to Save only for a greater s…
ryota-murakami Oct 22, 2026
c61be1d
merge: PR-1 review round 1, group A2 (F216 F217)
ryota-murakami Oct 22, 2026
161f738
test(test run): the passing preparation check asserts the member firs…
ryota-murakami Oct 22, 2026
2457eb5
fix(test run): the seal's transient codes derive from the file list (…
ryota-murakami Oct 22, 2026
f1d2cc1
fix(test run): the seal retry delay is injected, not a mutable field …
ryota-murakami Oct 22, 2026
f206505
fix(test run): a stop keeps an admission refusal (F222)
ryota-murakami Oct 22, 2026
b26d5b1
fix(test run): the declared column is tried first, the bound column s…
ryota-murakami Oct 22, 2026
1a124cb
test(wiki reader dom): compare the room's stills only once each has s…
ryota-murakami Oct 22, 2026
463d902
fix(test replay): Retry after a failed first debugger start restarts …
ryota-murakami Oct 22, 2026
0d90c22
merge: PR-1 review round 1, group B (F221 F225 F226 F228)
ryota-murakami Oct 22, 2026
d381277
fix(test run): a failed release is stored and thrown sanitized (F218)
ryota-murakami Oct 22, 2026
f799758
test(study workspace): the reopened test replay's waits use a ten-sec…
ryota-murakami Oct 22, 2026
a751f57
test(wiki update): a second window acts only after the first window s…
ryota-murakami Oct 22, 2026
67d8e6b
fix(test run): causes of status, action, continue, the receipt, the a…
ryota-murakami Oct 23, 2026
5444158
fix(test run): a runtime's own and its evidence's failure show no pri…
ryota-murakami Oct 23, 2026
ec213b9
merge: PR-1 review round 1, group A1 (F218 F219 F220 F222 F223 F224 F…
ryota-murakami Oct 23, 2026
d8790e5
test(study workspace): reopen the test replay through a new store onl…
ryota-murakami Oct 23, 2026
21f3a68
docs(agents): the bug-fix rule and the closing rule name the ship rev…
ryota-murakami Oct 23, 2026
125ad3c
fix(test replay): a failed first debugger start overtaken while its o…
ryota-murakami Oct 23, 2026
3beb381
test(test replay): the waits this PR added for the reader's results u…
ryota-murakami Oct 23, 2026
2c65f24
merge: PR-1 review round 2, group B (F237 F239)
ryota-murakami Oct 23, 2026
9d5c51b
test(study workspace): the reopen after a stopped historical debugger…
ryota-murakami Oct 23, 2026
46f1340
test(entry): observe the live entry's final arrangement from before t…
ryota-murakami Oct 23, 2026
5f71329
fix(test run): an article node with one citation is named as one sour…
ryota-murakami Oct 23, 2026
870b713
test(test run): start keys and study ids come from randomUUID, not li…
ryota-murakami Oct 23, 2026
a96ada1
fix(test run): MCP answers and test histories name no private path in…
ryota-murakami Oct 23, 2026
5c833cb
test(test run): a repository-browser run whose release failed offers …
ryota-murakami Oct 23, 2026
f7c6573
test(test run): every MCP answer path and a recording's broken-line r…
ryota-murakami Oct 23, 2026
0d336e5
fix(test run): a sealed history is removed only when its seal records…
ryota-murakami Oct 23, 2026
1350981
test(test run): the F256 test's start key comes from randomUUID, not …
ryota-murakami Oct 23, 2026
763bf0e
fix(test run): every MCP answer sanitizes domain errors too (F261); l…
ryota-murakami Oct 23, 2026
0ddd458
docs(readme): test replays: which runs offer read-history, kept and r…
ryota-murakami Oct 23, 2026
d10f77d
test(extension): the Save Learning Replay notice test's workspace sta…
ryota-murakami Oct 23, 2026
94aba4c
chore: bump version and changelog (v0.2.36.0)
ryota-murakami Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions .codex/restore-product-context.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,24 @@ const RESTORED_SECTIONS = ['## Mandatory entry', '## Owner directions in force']
* Returns one `## ` section of AGENTS.md, from its heading to the next `## ` heading.
*
* Why: the restored context carries only the sections that order the re-entry and keep the owner's scope, so it stays within the hosts' context limits.
* Lines are split on LF or CRLF (a Git for Windows checkout) and a heading matches despite trailing spaces or tabs; the section's own lines keep their trailing spaces, so a Markdown hard break is restored verbatim (F268).
* Caller: {@link restoreProductContext}.
*
* @param instructions - The current AGENTS.md text.
* @param heading - The exact `## ` heading line.
* @returns The section text without trailing blank lines.
* @throws When the heading is missing, so that a renamed section never restores silently without it.
* @param heading - The `## ` heading line.
* @returns The section text without trailing blank lines, or `undefined` when the heading is missing (renamed or removed).
* @example
* sectionOf('# A\n\n## Mandatory entry\n\nRead.\n\n## Next\n', '## Mandatory entry') // => '## Mandatory entry\n\nRead.'
* sectionOf('# A\r\n\r\n## Mandatory entry \r\n\r\nRead.\r\n\r\n## Next\r\n', '## Mandatory entry') // => '## Mandatory entry\n\nRead.'
*/
function sectionOf(instructions, heading) {
const lines = instructions.split('\n')
const start = lines.indexOf(heading)
if (start === -1)
throw new Error(`AGENTS.md is missing its ${heading.slice(3)} section.`)
const lines = instructions.split(/\r?\n/)
// Only the heading is compared without its trailing spaces; the section's lines are restored as they are, a Markdown hard break included.
const start = lines.findIndex((line) => line.trimEnd() === heading)
if (start === -1) return undefined
const next = lines.findIndex(
(line, index) => index > start && line.startsWith('## '),
)
return lines
.slice(start, next === -1 ? lines.length : next)
return [heading, ...lines.slice(start + 1, next === -1 ? lines.length : next)]
.join('\n')
.trimEnd()
}
Expand Down Expand Up @@ -57,11 +56,23 @@ async function restoreProductContext() {
"The owner's most important rule: before any other work after this compaction, whatever the task, read PRODUCT_GOAL.md in full, every line, and open the approved PNG (docs/designs/assets/codebook-product-spec-v9.png) with an image-viewing tool and check its identity against PRODUCT_GOAL.md's Design target; then read the active cycle Issue (the open Issue with the label `cycle`) with its comments and AGENTS.md in full from disk. A compacted summary or this reminder is not evidence of those reads or of the image inspection.",
'Reconcile the current source and affected acceptance gaps, then resume the latest owner-authorized task. Preserve pending owner instructions and any explicit pause; this reminder grants no additional work scope.',
]
const sections = RESTORED_SECTIONS.map((heading) =>
sectionOf(instructions, heading),
const found = RESTORED_SECTIONS.map((heading) => ({
heading,
text: sectionOf(instructions, heading),
}))
const missing = found.filter((section) => section.text === undefined)
const sections = found.flatMap((section) =>
section.text === undefined ? [] : [section.text],
)
// A renamed or removed section must never leave the owner's rule unprinted: the rule goes out, with the sections that were found, and names what is missing.
const missingNote = missing.length
? [
`AGENTS.md has no ${missing.map((section) => section.heading.slice(3)).join(' or ')} section under that exact heading; read AGENTS.md in full on disk now.`,
]
: []
const full = [
...rule,
...missingNote,
'These AGENTS.md sections follow verbatim; read the whole file on disk and resolve its relative links from the repository root:',
...sections,
].join('\n\n')
Expand All @@ -71,8 +82,13 @@ async function restoreProductContext() {
? full
: [
...rule,
...missingNote,
`AGENTS.md's ${RESTORED_SECTIONS.map((heading) => heading.slice(3)).join(' and ')} sections exceed this reminder's ${MAX_CONTEXT_CHARS}-character limit; read them in AGENTS.md on disk now.`,
].join('\n\n')
if (missing.length)
process.stderr.write(
`Codebook context restoration: AGENTS.md is missing its ${missing.map((section) => section.heading.slice(3)).join(' and ')} section.\n`,
)
process.stdout.write(
JSON.stringify({
hookSpecificOutput: {
Expand Down
107 changes: 99 additions & 8 deletions .codex/restore-product-context.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,13 @@ test('compaction restores the re-entry rule and the Mandatory entry and Owner di
context.length <= 10_000,
`context has ${context.length} characters`,
)
assert.ok(
context.indexOf(
'read PRODUCT_GOAL.md in full, every line, and open the approved PNG',
) < 2_000,
// Both bounds: a missing rule gives -1, which alone would pass `< 2_000` (F84).
// The hook's own sentence, not the phrase that AGENTS.md's Mandatory entry section repeats further down.
const rulePosition = context.indexOf(
"The owner's most important rule: before any other work after this compaction, whatever the task, read PRODUCT_GOAL.md in full, every line, and open the approved PNG",
)
assert.ok(rulePosition >= 0, 'the re-entry rule is missing')
assert.ok(rulePosition < 2_000, `the rule starts at ${rulePosition}`)
assert.ok(context.includes(agentsSection(agents, '## Mandatory entry')))
assert.ok(
context.includes(agentsSection(agents, '## Owner directions in force')),
Expand Down Expand Up @@ -134,22 +136,83 @@ test('an AGENTS.md whose restored sections outgrow the limit still restores the
assert.ok(!context.includes('Read the goal.\nRead the goal.'))
})

// Value: protects=a renamed or removed section being noticed; fails_when=the hook restores a context without the Owner directions section; no_claim=which sections AGENTS.md should have
test('an AGENTS.md without the Owner directions section reports failure without inventing restored context', () => {
// Value: protects=the owner's re-entry rule reaching the agent when a restored section is renamed or removed (F74); fails_when=the hook fails or prints nothing instead of printing the rule and naming the missing section; no_claim=which sections AGENTS.md should have
test('an AGENTS.md without the Owner directions section still restores the rule, the section it has, and names the missing one', () => {
// Arrange
const withoutDirections =
'# codebook agent instructions\n\n## Mandatory entry\n\nRead the goal.\n'
// Act
const result = runHookBeside(withoutDirections)
const context = JSON.parse(result.stdout).hookSpecificOutput.additionalContext
// Assert
assert.equal(result.status, 1)
assert.equal(result.stdout, '')
assert.equal(result.status, 0)
assert.match(
context,
/read PRODUCT_GOAL\.md in full, every line, and open the approved PNG/,
)
assert.ok(context.includes('## Mandatory entry\n\nRead the goal.'))
assert.match(
context,
/AGENTS\.md has no Owner directions in force section under that exact heading; read AGENTS\.md in full on disk now\./,
)
assert.match(
result.stderr,
/AGENTS\.md is missing its Owner directions in force section\./,
)
})

// Value: protects=the restored sections on a Git for Windows checkout (CRLF) and with trailing spaces after a heading (F74); fails_when=a CRLF or padded heading is not found, so the sections are dropped; no_claim=the wording of the sections
test('an AGENTS.md with CRLF line ends and padded headings restores both sections whole', () => {
// Arrange
const crlf = [
'# codebook agent instructions',
'',
'## Mandatory entry ',
'',
'Read the goal.',
'',
'## Owner directions in force\t',
'',
'Keep the owner scope.',
'',
'## Native QA',
'',
'Record windows.',
'',
].join('\r\n')
// Act
const result = runHookBeside(crlf)
const context = JSON.parse(result.stdout).hookSpecificOutput.additionalContext
// Assert
assert.equal(result.status, 0)
assert.equal(result.stderr, '')
assert.ok(context.includes('## Mandatory entry\n\nRead the goal.'))
assert.ok(
context.includes('## Owner directions in force\n\nKeep the owner scope.'),
)
assert.ok(!context.includes('Record windows.'))
assert.ok(!context.includes('\r'))
})

// Value: protects=the owner's rule when every restored heading was renamed (F74); fails_when=the hook prints nothing or fails; no_claim=any section text
test('an AGENTS.md whose restored headings were all renamed still restores the rule and names both missing sections', () => {
// Arrange
const renamed = '# codebook agent instructions\n\n## Entry\n\nRead.\n'
// Act
const result = runHookBeside(renamed)
const context = JSON.parse(result.stdout).hookSpecificOutput.additionalContext
// Assert
assert.equal(result.status, 0)
assert.match(
context,
/read PRODUCT_GOAL\.md in full, every line, and open the approved PNG/,
)
assert.match(
context,
/AGENTS\.md has no Mandatory entry or Owner directions in force section under that exact heading/,
)
})

test('startup, resume and other events do not add compaction instructions', () => {
// Arrange
const events = [
Expand Down Expand Up @@ -196,3 +259,31 @@ test('the repository registers only compaction restoration through a root-resolv
)
assert.equal(config.hooks.SessionStart[0].hooks[0].timeout, 5)
})

// Value: protects=the restored sections staying verbatim: a section line that ends in two spaces (a Markdown hard break) keeps them, while its heading still matches with trailing spaces (F268); fails_when=every line loses its trailing spaces, so the restored text differs from AGENTS.md; why_new=PR-1's /ship Claude adversarial review, cycle 3: the hook trimmed every line to match its headings; seam=none; no_claim=the wording of the sections
test('a restored section keeps the trailing spaces of its lines', () => {
// Arrange
const withHardBreak = [
'# codebook agent instructions',
'',
'## Mandatory entry ',
'',
'Read the goal, ',
'then the image.',
'',
'## Owner directions in force',
'',
'Keep the owner scope.',
'',
].join('\n')

// Act
const result = runHookBeside(withHardBreak)
const context = JSON.parse(result.stdout).hookSpecificOutput.additionalContext

// Assert
assert.equal(result.status, 0)
assert.ok(
context.includes('## Mandatory entry\n\nRead the goal, \nthen the image.'),
)
})
2 changes: 2 additions & 0 deletions .github/workflows/storage-locks-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ on:
- extension/src/tests/runBoundedQuery.test.ts
- extension/src/tests/runtime-profileProcesses.test.ts
- extension/src/tests/utils/answerKernelBootId.ts
- extension/src/tests/utils/answeredAtOnce.ts
- extension/src/tests/utils/createLinkIfAllowed.ts
- extension/src/tests/utils/fileSystemWhoseLinksResistRemoval.ts
- extension/src/tests/utils/fileSystemWithLargeFileIds.ts
Expand All @@ -79,6 +80,7 @@ on:
- extension/src/tests/utils/requireOwnPidNamespace.ts
- extension/src/tests/utils/standInForPs.ts
- extension/src/tests/utils/startOfKilledWindow.ts
- extension/src/tests/utils/within.ts
- extension/src/tests/withStorageLock.test.ts
- extension/src/utils/atomicPrivateBytes.ts
- extension/src/utils/atomicPrivateWrite.ts
Expand Down
Loading