📄 Say that XMD scans for credentials by default, and prove it over each backend - #575

Merged
taras merged 1 commit into
mainfrom
agent/issue-199-final
Aug 24, 2026
Merged

📄 Say that XMD scans for credentials by default, and prove it over each backend#575
taras merged 1 commit into
mainfrom
agent/issue-199-final

Conversation

@taras

Copy link
Copy Markdown
Owner

Why

Closes#199. Secret detection has been on by default since slice 2, but the
website still taught it as something a host brings, and the CLI reference did
not mention the opt-out at all. Anyone reading the docs to decide whether XMD
protects their journal came away with the wrong answer.

The test corpus had the matching gap: the real default scanner was only ever
proven over InMemoryStream. Every file and HTTP case installed a
test-supplied gate instead, so nothing showed that execute()'s own scanner
reaches those two backends.

What changes

Before:/docs/journal opened with guardDurableStream and a
caller-supplied containsCredential placeholder, and told the reader that
"deciding what counts as a secret is your call, not this library's."
/docs/reference listed five CLI options and no way to turn detection off.

After:/docs/journal opens with the built-in policy and states the
observable contract — default-on offline scanning installed before the first
live event; what a finding does to the offending append and to the run; output
withholding; diagnostics that name a rule and never a value; replay that does
not rescan; and the host-only opt-out as a dangerous diagnostic escape hatch.
guardDurableStream follows it as the generic decorator it is.
/docs/reference lists --no-secret-detection with the same invocation-wide
semantics.

No production code changed. Behavior is exactly what shipped in #329, #330 and
#573.

Review guide

Start with:site/routes/docs/journal.tsx

Then review:

  1. site/routes/docs/reference.tsx — the new CLI option entry
  2. packages/core/tests/guarded-journal.test.ts — the new
    describe("the default scanner over a persistence backend") block

Look carefully at:

  • The two new tests pass the backend directly to execute(). If either one
    grows a guardDurableStream wrapper, it stops testing the thing it exists to
    test.
  • Both assert an independently safe Close(err) rather than an empty backend.

What must stay true

  • The website describes the default, not a custom gate. Checked by reading
    the rendered page; site/deno task check and deno task build keep it
    compiling and formatted.
  • --no-secret-detection stays the only spelling that disables detection.
    Enforced by packages/cli/src/cli.ts and checked by
    packages/cli/tests/secret-detection-cli.test.ts; the docs now state it.
  • A rejection never implies an empty journal. Both new tests assert the
    offending event is absent and that close(root) with status err is
    present.
  • No usable credential enters the repository. The canary is assembled at run
    time from an alphabet slice; nothing reads an environment variable, Git
    credential, or user configuration.

How to verify it

  • keeps a credential out of a file backend proves the real default scanner
    rejects a credential before file persistence, and sweeps the JSONL bytes on
    disk. It fails if the scanner is not installed, if the rejected import reaches
    the backend, or if a partial record is written.
  • keeps a credential out of an HTTP backend proves the same before HTTP
    persistence, reading back through the stream rather than anything the run
    retained.
  • Both assert the error's message and stack are canary-free, so a diagnostic
    that quoted the matched value would fail them.

Mutation evidence. On a throwaway copy of the file with the two new
execute() calls changed to secretDetection: false and nothing else touched,
both new cases failed at expect(result.ok).toBe(false) while the five existing
cases still passed. They discriminate the default rather than an incidental
refusal.

Commands run at 74ef1d7d, after one deno task setup in a fresh worktree:

Working directoryCommandResult
rootdeno task test packages/core/tests/secret-detection.test.ts packages/core/tests/guarded-journal.test.tsok | 3 passed (51 steps) | 0 failed
rootpnpm exec tsx --tsconfig tsconfig.node.json --test --test-concurrency=1 <same two files>tests 43 / pass 43 / fail 0
rootbun test --timeout=300000 <same two files>43 pass / 0 fail
rootbun test --timeout=300000 -t "keeps a credential out of" packages/core/tests/guarded-journal.test.ts2 pass / 5 filtered out / 0 fail / 18 expect() calls
site/deno task checkexit 0
site/deno task build✓ built in 1.38s
rootdeno task lintexit 0, 0 errors
rootdeno task checkexit 0
rootdeno task check:jsrSuccess Dry run complete

The fourth row is not redundant. bun test prints only counts, so a suite that
silently skipped the two new cases would also report 43 pass; the filtered run
is what shows they ran under Bun and that their assertions executed.

Scope

Included

  • The /docs/journal rewrite and the /docs/reference CLI option entry.
  • Two default-scanner regressions, one per missing backend.

Intentionally unchanged

  • No production code. No scanner rule, finding shape, policy authority,
    durable event shape, output bridging, settlement, replay, or persistence
    adapter is touched.
  • No spec or architecture change.specs/executable-mdx-spec.md §8.1
    already states this contract; the website now matches it.
  • No CI change. The compiled-binary default/opt-out smoke in ci.yml stays
    as it is and remains the authority for that surface.
  • The memory permutations are not duplicated.secret-detection.test.ts
    already proves diagnostics, output withholding, settlement, replay and the
    disabled control. Those are execution-owned behavior, not backend variants, so
    each backend gets one focused case.
  • No runtime exclusion added. Both files stay out of
    scripts/runtime-test-exclusions.ts and run under all three runtimes.
  • The docs nav still reads "Journal gates". The page now leads with the
    built-in policy, so the label under-describes it, but renaming would touch
    site/routes/docs/_layout.tsx and site/routes/docs/agents.tsx — out of scope
    here, and flagged rather than done quietly.

Risks and limitations

  • The website is prose, so its accuracy is a reading rather than a test. Both
    pages were rendered from the built site and read as an operator would see them;
    every claim traces to specs/executable-mdx-spec.md §8.1 or to
    packages/cli/src/cli.ts, including the warning text quoted verbatim.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

…ch backend
The journal-gates page still described secret detection as something a host
brings: a `containsCredential` placeholder inside `guardDurableStream`. Every
execution has scanned by default since #199, so the page taught the wrong
common path, and the CLI reference did not mention the opt-out at all.
The page now leads with the built-in policy — default-on offline scanning
installed before the first live event, what a finding does to the offending
append and to the run, output withholding, the diagnostic that names a rule
and never a value, and replay that does not rescan. `secretDetection: false`
and `--no-secret-detection` appear as a dangerous diagnostic escape hatch with
the warning the CLI actually writes, not as remediation. `guardDurableStream`
keeps its gate-ordering, per-event and replay explanation, now as the generic
decorator it is, and its example refuses an oversized event rather than
deciding what a credential is. The reference lists the option with the same
invocation-wide semantics.
Two regressions cover the real default scanner over the two backends the
memory cases could not reach. Each puts a synthetic, run-time-assembled
credential in the root source, passes the backend straight to `execute()`
with default options, and asserts `Err(SecretDetectedError)`, the absence of
the rejected import, an independently safe `Close(err)`, and the canary
nowhere in the persisted events or the error's readable fields — through the
JSONL bytes on disk for the file backend, and through a read-back for HTTP.
@github-actions

Copy link
Copy Markdown

PR #575: 📄 Say that XMD scans for credentials by default, and prove it over each backend

3 files, +215 / -24

Scope

✅ PR scope looks good.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 24, 2026 13:58
@taras
taras merged commit 042e240 into mainAug 24, 2026
28 checks passed
@taras
taras deleted the agent/issue-199-final branch August 24, 2026 14:07
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
Sign up for freeto 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.

Reject secrets before journal persistence by default

1 participant

@taras
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

📄 Say that XMD scans for credentials by default, and prove it over each backend - #575

Merged
taras merged 1 commit into
mainfrom
agent/issue-199-final
Aug 24, 2026
Merged

📄 Say that XMD scans for credentials by default, and prove it over each backend#575
taras merged 1 commit into
mainfrom
agent/issue-199-final

Conversation

@taras

Copy link
Copy Markdown
Owner

Why

Closes#199. Secret detection has been on by default since slice 2, but the
website still taught it as something a host brings, and the CLI reference did
not mention the opt-out at all. Anyone reading the docs to decide whether XMD
protects their journal came away with the wrong answer.

The test corpus had the matching gap: the real default scanner was only ever
proven over InMemoryStream. Every file and HTTP case installed a
test-supplied gate instead, so nothing showed that execute()'s own scanner
reaches those two backends.

What changes

Before:/docs/journal opened with guardDurableStream and a
caller-supplied containsCredential placeholder, and told the reader that
"deciding what counts as a secret is your call, not this library's."
/docs/reference listed five CLI options and no way to turn detection off.

After:/docs/journal opens with the built-in policy and states the
observable contract — default-on offline scanning installed before the first
live event; what a finding does to the offending append and to the run; output
withholding; diagnostics that name a rule and never a value; replay that does
not rescan; and the host-only opt-out as a dangerous diagnostic escape hatch.
guardDurableStream follows it as the generic decorator it is.
/docs/reference lists --no-secret-detection with the same invocation-wide
semantics.

No production code changed. Behavior is exactly what shipped in #329, #330 and
#573.

Review guide

Start with:site/routes/docs/journal.tsx

Then review:

  1. site/routes/docs/reference.tsx — the new CLI option entry
  2. packages/core/tests/guarded-journal.test.ts — the new
    describe("the default scanner over a persistence backend") block

Look carefully at:

  • The two new tests pass the backend directly to execute(). If either one
    grows a guardDurableStream wrapper, it stops testing the thing it exists to
    test.
  • Both assert an independently safe Close(err) rather than an empty backend.

What must stay true

  • The website describes the default, not a custom gate. Checked by reading
    the rendered page; site/deno task check and deno task build keep it
    compiling and formatted.
  • --no-secret-detection stays the only spelling that disables detection.
    Enforced by packages/cli/src/cli.ts and checked by
    packages/cli/tests/secret-detection-cli.test.ts; the docs now state it.
  • A rejection never implies an empty journal. Both new tests assert the
    offending event is absent and that close(root) with status err is
    present.
  • No usable credential enters the repository. The canary is assembled at run
    time from an alphabet slice; nothing reads an environment variable, Git
    credential, or user configuration.

How to verify it

  • keeps a credential out of a file backend proves the real default scanner
    rejects a credential before file persistence, and sweeps the JSONL bytes on
    disk. It fails if the scanner is not installed, if the rejected import reaches
    the backend, or if a partial record is written.
  • keeps a credential out of an HTTP backend proves the same before HTTP
    persistence, reading back through the stream rather than anything the run
    retained.
  • Both assert the error's message and stack are canary-free, so a diagnostic
    that quoted the matched value would fail them.

Mutation evidence. On a throwaway copy of the file with the two new
execute() calls changed to secretDetection: false and nothing else touched,
both new cases failed at expect(result.ok).toBe(false) while the five existing
cases still passed. They discriminate the default rather than an incidental
refusal.

Commands run at 74ef1d7d, after one deno task setup in a fresh worktree:

Working directoryCommandResult
rootdeno task test packages/core/tests/secret-detection.test.ts packages/core/tests/guarded-journal.test.tsok | 3 passed (51 steps) | 0 failed
rootpnpm exec tsx --tsconfig tsconfig.node.json --test --test-concurrency=1 <same two files>tests 43 / pass 43 / fail 0
rootbun test --timeout=300000 <same two files>43 pass / 0 fail
rootbun test --timeout=300000 -t "keeps a credential out of" packages/core/tests/guarded-journal.test.ts2 pass / 5 filtered out / 0 fail / 18 expect() calls
site/deno task checkexit 0
site/deno task build✓ built in 1.38s
rootdeno task lintexit 0, 0 errors
rootdeno task checkexit 0
rootdeno task check:jsrSuccess Dry run complete

The fourth row is not redundant. bun test prints only counts, so a suite that
silently skipped the two new cases would also report 43 pass; the filtered run
is what shows they ran under Bun and that their assertions executed.

Scope

Included

  • The /docs/journal rewrite and the /docs/reference CLI option entry.
  • Two default-scanner regressions, one per missing backend.

Intentionally unchanged

  • No production code. No scanner rule, finding shape, policy authority,
    durable event shape, output bridging, settlement, replay, or persistence
    adapter is touched.
  • No spec or architecture change.specs/executable-mdx-spec.md §8.1
    already states this contract; the website now matches it.
  • No CI change. The compiled-binary default/opt-out smoke in ci.yml stays
    as it is and remains the authority for that surface.
  • The memory permutations are not duplicated.secret-detection.test.ts
    already proves diagnostics, output withholding, settlement, replay and the
    disabled control. Those are execution-owned behavior, not backend variants, so
    each backend gets one focused case.
  • No runtime exclusion added. Both files stay out of
    scripts/runtime-test-exclusions.ts and run under all three runtimes.
  • The docs nav still reads "Journal gates". The page now leads with the
    built-in policy, so the label under-describes it, but renaming would touch
    site/routes/docs/_layout.tsx and site/routes/docs/agents.tsx — out of scope
    here, and flagged rather than done quietly.

Risks and limitations

  • The website is prose, so its accuracy is a reading rather than a test. Both
    pages were rendered from the built site and read as an operator would see them;
    every claim traces to specs/executable-mdx-spec.md §8.1 or to
    packages/cli/src/cli.ts, including the warning text quoted verbatim.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

…ch backend
The journal-gates page still described secret detection as something a host
brings: a `containsCredential` placeholder inside `guardDurableStream`. Every
execution has scanned by default since #199, so the page taught the wrong
common path, and the CLI reference did not mention the opt-out at all.
The page now leads with the built-in policy — default-on offline scanning
installed before the first live event, what a finding does to the offending
append and to the run, output withholding, the diagnostic that names a rule
and never a value, and replay that does not rescan. `secretDetection: false`
and `--no-secret-detection` appear as a dangerous diagnostic escape hatch with
the warning the CLI actually writes, not as remediation. `guardDurableStream`
keeps its gate-ordering, per-event and replay explanation, now as the generic
decorator it is, and its example refuses an oversized event rather than
deciding what a credential is. The reference lists the option with the same
invocation-wide semantics.
Two regressions cover the real default scanner over the two backends the
memory cases could not reach. Each puts a synthetic, run-time-assembled
credential in the root source, passes the backend straight to `execute()`
with default options, and asserts `Err(SecretDetectedError)`, the absence of
the rejected import, an independently safe `Close(err)`, and the canary
nowhere in the persisted events or the error's readable fields — through the
JSONL bytes on disk for the file backend, and through a read-back for HTTP.
@github-actions

Copy link
Copy Markdown

PR #575: 📄 Say that XMD scans for credentials by default, and prove it over each backend

3 files, +215 / -24

Scope

✅ PR scope looks good.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 24, 2026 13:58
@taras
taras merged commit 042e240 into mainAug 24, 2026
28 checks passed
@taras
taras deleted the agent/issue-199-final branch August 24, 2026 14:07
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
Sign up for freeto 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.

Reject secrets before journal persistence by default

1 participant

@taras
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

📄 Say that XMD scans for credentials by default, and prove it over each backend - #575

Merged
taras merged 1 commit into
mainfrom
agent/issue-199-final
Aug 24, 2026
Merged

📄 Say that XMD scans for credentials by default, and prove it over each backend#575
taras merged 1 commit into
mainfrom
agent/issue-199-final

Conversation

@taras

Copy link
Copy Markdown
Owner

Why

Closes#199. Secret detection has been on by default since slice 2, but the
website still taught it as something a host brings, and the CLI reference did
not mention the opt-out at all. Anyone reading the docs to decide whether XMD
protects their journal came away with the wrong answer.

The test corpus had the matching gap: the real default scanner was only ever
proven over InMemoryStream. Every file and HTTP case installed a
test-supplied gate instead, so nothing showed that execute()'s own scanner
reaches those two backends.

What changes

Before:/docs/journal opened with guardDurableStream and a
caller-supplied containsCredential placeholder, and told the reader that
"deciding what counts as a secret is your call, not this library's."
/docs/reference listed five CLI options and no way to turn detection off.

After:/docs/journal opens with the built-in policy and states the
observable contract — default-on offline scanning installed before the first
live event; what a finding does to the offending append and to the run; output
withholding; diagnostics that name a rule and never a value; replay that does
not rescan; and the host-only opt-out as a dangerous diagnostic escape hatch.
guardDurableStream follows it as the generic decorator it is.
/docs/reference lists --no-secret-detection with the same invocation-wide
semantics.

No production code changed. Behavior is exactly what shipped in #329, #330 and
#573.

Review guide

Start with:site/routes/docs/journal.tsx

Then review:

  1. site/routes/docs/reference.tsx — the new CLI option entry
  2. packages/core/tests/guarded-journal.test.ts — the new
    describe("the default scanner over a persistence backend") block

Look carefully at:

  • The two new tests pass the backend directly to execute(). If either one
    grows a guardDurableStream wrapper, it stops testing the thing it exists to
    test.
  • Both assert an independently safe Close(err) rather than an empty backend.

What must stay true

  • The website describes the default, not a custom gate. Checked by reading
    the rendered page; site/deno task check and deno task build keep it
    compiling and formatted.
  • --no-secret-detection stays the only spelling that disables detection.
    Enforced by packages/cli/src/cli.ts and checked by
    packages/cli/tests/secret-detection-cli.test.ts; the docs now state it.
  • A rejection never implies an empty journal. Both new tests assert the
    offending event is absent and that close(root) with status err is
    present.
  • No usable credential enters the repository. The canary is assembled at run
    time from an alphabet slice; nothing reads an environment variable, Git
    credential, or user configuration.

How to verify it

  • keeps a credential out of a file backend proves the real default scanner
    rejects a credential before file persistence, and sweeps the JSONL bytes on
    disk. It fails if the scanner is not installed, if the rejected import reaches
    the backend, or if a partial record is written.
  • keeps a credential out of an HTTP backend proves the same before HTTP
    persistence, reading back through the stream rather than anything the run
    retained.
  • Both assert the error's message and stack are canary-free, so a diagnostic
    that quoted the matched value would fail them.

Mutation evidence. On a throwaway copy of the file with the two new
execute() calls changed to secretDetection: false and nothing else touched,
both new cases failed at expect(result.ok).toBe(false) while the five existing
cases still passed. They discriminate the default rather than an incidental
refusal.

Commands run at 74ef1d7d, after one deno task setup in a fresh worktree:

Working directoryCommandResult
rootdeno task test packages/core/tests/secret-detection.test.ts packages/core/tests/guarded-journal.test.tsok | 3 passed (51 steps) | 0 failed
rootpnpm exec tsx --tsconfig tsconfig.node.json --test --test-concurrency=1 <same two files>tests 43 / pass 43 / fail 0
rootbun test --timeout=300000 <same two files>43 pass / 0 fail
rootbun test --timeout=300000 -t "keeps a credential out of" packages/core/tests/guarded-journal.test.ts2 pass / 5 filtered out / 0 fail / 18 expect() calls
site/deno task checkexit 0
site/deno task build✓ built in 1.38s
rootdeno task lintexit 0, 0 errors
rootdeno task checkexit 0
rootdeno task check:jsrSuccess Dry run complete

The fourth row is not redundant. bun test prints only counts, so a suite that
silently skipped the two new cases would also report 43 pass; the filtered run
is what shows they ran under Bun and that their assertions executed.

Scope

Included

  • The /docs/journal rewrite and the /docs/reference CLI option entry.
  • Two default-scanner regressions, one per missing backend.

Intentionally unchanged

  • No production code. No scanner rule, finding shape, policy authority,
    durable event shape, output bridging, settlement, replay, or persistence
    adapter is touched.
  • No spec or architecture change.specs/executable-mdx-spec.md §8.1
    already states this contract; the website now matches it.
  • No CI change. The compiled-binary default/opt-out smoke in ci.yml stays
    as it is and remains the authority for that surface.
  • The memory permutations are not duplicated.secret-detection.test.ts
    already proves diagnostics, output withholding, settlement, replay and the
    disabled control. Those are execution-owned behavior, not backend variants, so
    each backend gets one focused case.
  • No runtime exclusion added. Both files stay out of
    scripts/runtime-test-exclusions.ts and run under all three runtimes.
  • The docs nav still reads "Journal gates". The page now leads with the
    built-in policy, so the label under-describes it, but renaming would touch
    site/routes/docs/_layout.tsx and site/routes/docs/agents.tsx — out of scope
    here, and flagged rather than done quietly.

Risks and limitations

  • The website is prose, so its accuracy is a reading rather than a test. Both
    pages were rendered from the built site and read as an operator would see them;
    every claim traces to specs/executable-mdx-spec.md §8.1 or to
    packages/cli/src/cli.ts, including the warning text quoted verbatim.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

…ch backend
The journal-gates page still described secret detection as something a host
brings: a `containsCredential` placeholder inside `guardDurableStream`. Every
execution has scanned by default since #199, so the page taught the wrong
common path, and the CLI reference did not mention the opt-out at all.
The page now leads with the built-in policy — default-on offline scanning
installed before the first live event, what a finding does to the offending
append and to the run, output withholding, the diagnostic that names a rule
and never a value, and replay that does not rescan. `secretDetection: false`
and `--no-secret-detection` appear as a dangerous diagnostic escape hatch with
the warning the CLI actually writes, not as remediation. `guardDurableStream`
keeps its gate-ordering, per-event and replay explanation, now as the generic
decorator it is, and its example refuses an oversized event rather than
deciding what a credential is. The reference lists the option with the same
invocation-wide semantics.
Two regressions cover the real default scanner over the two backends the
memory cases could not reach. Each puts a synthetic, run-time-assembled
credential in the root source, passes the backend straight to `execute()`
with default options, and asserts `Err(SecretDetectedError)`, the absence of
the rejected import, an independently safe `Close(err)`, and the canary
nowhere in the persisted events or the error's readable fields — through the
JSONL bytes on disk for the file backend, and through a read-back for HTTP.
@github-actions

Copy link
Copy Markdown

PR #575: 📄 Say that XMD scans for credentials by default, and prove it over each backend

3 files, +215 / -24

Scope

✅ PR scope looks good.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 24, 2026 13:58
@taras
taras merged commit 042e240 into mainAug 24, 2026
28 checks passed
@taras
taras deleted the agent/issue-199-final branch August 24, 2026 14:07
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
Sign up for freeto 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.

Reject secrets before journal persistence by default

1 participant

@taras
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

📄 Say that XMD scans for credentials by default, and prove it over each backend - #575

Merged
taras merged 1 commit into
mainfrom
agent/issue-199-final
Aug 24, 2026
Merged

📄 Say that XMD scans for credentials by default, and prove it over each backend#575
taras merged 1 commit into
mainfrom
agent/issue-199-final

Conversation

@taras

Copy link
Copy Markdown
Owner

Why

Closes#199. Secret detection has been on by default since slice 2, but the
website still taught it as something a host brings, and the CLI reference did
not mention the opt-out at all. Anyone reading the docs to decide whether XMD
protects their journal came away with the wrong answer.

The test corpus had the matching gap: the real default scanner was only ever
proven over InMemoryStream. Every file and HTTP case installed a
test-supplied gate instead, so nothing showed that execute()'s own scanner
reaches those two backends.

What changes

Before:/docs/journal opened with guardDurableStream and a
caller-supplied containsCredential placeholder, and told the reader that
"deciding what counts as a secret is your call, not this library's."
/docs/reference listed five CLI options and no way to turn detection off.

After:/docs/journal opens with the built-in policy and states the
observable contract — default-on offline scanning installed before the first
live event; what a finding does to the offending append and to the run; output
withholding; diagnostics that name a rule and never a value; replay that does
not rescan; and the host-only opt-out as a dangerous diagnostic escape hatch.
guardDurableStream follows it as the generic decorator it is.
/docs/reference lists --no-secret-detection with the same invocation-wide
semantics.

No production code changed. Behavior is exactly what shipped in #329, #330 and
#573.

Review guide

Start with:site/routes/docs/journal.tsx

Then review:

  1. site/routes/docs/reference.tsx — the new CLI option entry
  2. packages/core/tests/guarded-journal.test.ts — the new
    describe("the default scanner over a persistence backend") block

Look carefully at:

  • The two new tests pass the backend directly to execute(). If either one
    grows a guardDurableStream wrapper, it stops testing the thing it exists to
    test.
  • Both assert an independently safe Close(err) rather than an empty backend.

What must stay true

  • The website describes the default, not a custom gate. Checked by reading
    the rendered page; site/deno task check and deno task build keep it
    compiling and formatted.
  • --no-secret-detection stays the only spelling that disables detection.
    Enforced by packages/cli/src/cli.ts and checked by
    packages/cli/tests/secret-detection-cli.test.ts; the docs now state it.
  • A rejection never implies an empty journal. Both new tests assert the
    offending event is absent and that close(root) with status err is
    present.
  • No usable credential enters the repository. The canary is assembled at run
    time from an alphabet slice; nothing reads an environment variable, Git
    credential, or user configuration.

How to verify it

  • keeps a credential out of a file backend proves the real default scanner
    rejects a credential before file persistence, and sweeps the JSONL bytes on
    disk. It fails if the scanner is not installed, if the rejected import reaches
    the backend, or if a partial record is written.
  • keeps a credential out of an HTTP backend proves the same before HTTP
    persistence, reading back through the stream rather than anything the run
    retained.
  • Both assert the error's message and stack are canary-free, so a diagnostic
    that quoted the matched value would fail them.

Mutation evidence. On a throwaway copy of the file with the two new
execute() calls changed to secretDetection: false and nothing else touched,
both new cases failed at expect(result.ok).toBe(false) while the five existing
cases still passed. They discriminate the default rather than an incidental
refusal.

Commands run at 74ef1d7d, after one deno task setup in a fresh worktree:

Working directoryCommandResult
rootdeno task test packages/core/tests/secret-detection.test.ts packages/core/tests/guarded-journal.test.tsok | 3 passed (51 steps) | 0 failed
rootpnpm exec tsx --tsconfig tsconfig.node.json --test --test-concurrency=1 <same two files>tests 43 / pass 43 / fail 0
rootbun test --timeout=300000 <same two files>43 pass / 0 fail
rootbun test --timeout=300000 -t "keeps a credential out of" packages/core/tests/guarded-journal.test.ts2 pass / 5 filtered out / 0 fail / 18 expect() calls
site/deno task checkexit 0
site/deno task build✓ built in 1.38s
rootdeno task lintexit 0, 0 errors
rootdeno task checkexit 0
rootdeno task check:jsrSuccess Dry run complete

The fourth row is not redundant. bun test prints only counts, so a suite that
silently skipped the two new cases would also report 43 pass; the filtered run
is what shows they ran under Bun and that their assertions executed.

Scope

Included

  • The /docs/journal rewrite and the /docs/reference CLI option entry.
  • Two default-scanner regressions, one per missing backend.

Intentionally unchanged

  • No production code. No scanner rule, finding shape, policy authority,
    durable event shape, output bridging, settlement, replay, or persistence
    adapter is touched.
  • No spec or architecture change.specs/executable-mdx-spec.md §8.1
    already states this contract; the website now matches it.
  • No CI change. The compiled-binary default/opt-out smoke in ci.yml stays
    as it is and remains the authority for that surface.
  • The memory permutations are not duplicated.secret-detection.test.ts
    already proves diagnostics, output withholding, settlement, replay and the
    disabled control. Those are execution-owned behavior, not backend variants, so
    each backend gets one focused case.
  • No runtime exclusion added. Both files stay out of
    scripts/runtime-test-exclusions.ts and run under all three runtimes.
  • The docs nav still reads "Journal gates". The page now leads with the
    built-in policy, so the label under-describes it, but renaming would touch
    site/routes/docs/_layout.tsx and site/routes/docs/agents.tsx — out of scope
    here, and flagged rather than done quietly.

Risks and limitations

  • The website is prose, so its accuracy is a reading rather than a test. Both
    pages were rendered from the built site and read as an operator would see them;
    every claim traces to specs/executable-mdx-spec.md §8.1 or to
    packages/cli/src/cli.ts, including the warning text quoted verbatim.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

…ch backend
The journal-gates page still described secret detection as something a host
brings: a `containsCredential` placeholder inside `guardDurableStream`. Every
execution has scanned by default since #199, so the page taught the wrong
common path, and the CLI reference did not mention the opt-out at all.
The page now leads with the built-in policy — default-on offline scanning
installed before the first live event, what a finding does to the offending
append and to the run, output withholding, the diagnostic that names a rule
and never a value, and replay that does not rescan. `secretDetection: false`
and `--no-secret-detection` appear as a dangerous diagnostic escape hatch with
the warning the CLI actually writes, not as remediation. `guardDurableStream`
keeps its gate-ordering, per-event and replay explanation, now as the generic
decorator it is, and its example refuses an oversized event rather than
deciding what a credential is. The reference lists the option with the same
invocation-wide semantics.
Two regressions cover the real default scanner over the two backends the
memory cases could not reach. Each puts a synthetic, run-time-assembled
credential in the root source, passes the backend straight to `execute()`
with default options, and asserts `Err(SecretDetectedError)`, the absence of
the rejected import, an independently safe `Close(err)`, and the canary
nowhere in the persisted events or the error's readable fields — through the
JSONL bytes on disk for the file backend, and through a read-back for HTTP.
@github-actions

Copy link
Copy Markdown

PR #575: 📄 Say that XMD scans for credentials by default, and prove it over each backend

3 files, +215 / -24

Scope

✅ PR scope looks good.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 24, 2026 13:58
@taras
taras merged commit 042e240 into mainAug 24, 2026
28 checks passed
@taras
taras deleted the agent/issue-199-final branch August 24, 2026 14:07
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
Sign up for freeto 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.

Reject secrets before journal persistence by default

1 participant

@taras
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

📄 Say that XMD scans for credentials by default, and prove it over each backend - #575

Merged
taras merged 1 commit into
mainfrom
agent/issue-199-final
Aug 24, 2026
Merged

📄 Say that XMD scans for credentials by default, and prove it over each backend#575
taras merged 1 commit into
mainfrom
agent/issue-199-final

Conversation

@taras

Copy link
Copy Markdown
Owner

Why

Closes#199. Secret detection has been on by default since slice 2, but the
website still taught it as something a host brings, and the CLI reference did
not mention the opt-out at all. Anyone reading the docs to decide whether XMD
protects their journal came away with the wrong answer.

The test corpus had the matching gap: the real default scanner was only ever
proven over InMemoryStream. Every file and HTTP case installed a
test-supplied gate instead, so nothing showed that execute()'s own scanner
reaches those two backends.

What changes

Before:/docs/journal opened with guardDurableStream and a
caller-supplied containsCredential placeholder, and told the reader that
"deciding what counts as a secret is your call, not this library's."
/docs/reference listed five CLI options and no way to turn detection off.

After:/docs/journal opens with the built-in policy and states the
observable contract — default-on offline scanning installed before the first
live event; what a finding does to the offending append and to the run; output
withholding; diagnostics that name a rule and never a value; replay that does
not rescan; and the host-only opt-out as a dangerous diagnostic escape hatch.
guardDurableStream follows it as the generic decorator it is.
/docs/reference lists --no-secret-detection with the same invocation-wide
semantics.

No production code changed. Behavior is exactly what shipped in #329, #330 and
#573.

Review guide

Start with:site/routes/docs/journal.tsx

Then review:

  1. site/routes/docs/reference.tsx — the new CLI option entry
  2. packages/core/tests/guarded-journal.test.ts — the new
    describe("the default scanner over a persistence backend") block

Look carefully at:

  • The two new tests pass the backend directly to execute(). If either one
    grows a guardDurableStream wrapper, it stops testing the thing it exists to
    test.
  • Both assert an independently safe Close(err) rather than an empty backend.

What must stay true

  • The website describes the default, not a custom gate. Checked by reading
    the rendered page; site/deno task check and deno task build keep it
    compiling and formatted.
  • --no-secret-detection stays the only spelling that disables detection.
    Enforced by packages/cli/src/cli.ts and checked by
    packages/cli/tests/secret-detection-cli.test.ts; the docs now state it.
  • A rejection never implies an empty journal. Both new tests assert the
    offending event is absent and that close(root) with status err is
    present.
  • No usable credential enters the repository. The canary is assembled at run
    time from an alphabet slice; nothing reads an environment variable, Git
    credential, or user configuration.

How to verify it

  • keeps a credential out of a file backend proves the real default scanner
    rejects a credential before file persistence, and sweeps the JSONL bytes on
    disk. It fails if the scanner is not installed, if the rejected import reaches
    the backend, or if a partial record is written.
  • keeps a credential out of an HTTP backend proves the same before HTTP
    persistence, reading back through the stream rather than anything the run
    retained.
  • Both assert the error's message and stack are canary-free, so a diagnostic
    that quoted the matched value would fail them.

Mutation evidence. On a throwaway copy of the file with the two new
execute() calls changed to secretDetection: false and nothing else touched,
both new cases failed at expect(result.ok).toBe(false) while the five existing
cases still passed. They discriminate the default rather than an incidental
refusal.

Commands run at 74ef1d7d, after one deno task setup in a fresh worktree:

Working directoryCommandResult
rootdeno task test packages/core/tests/secret-detection.test.ts packages/core/tests/guarded-journal.test.tsok | 3 passed (51 steps) | 0 failed
rootpnpm exec tsx --tsconfig tsconfig.node.json --test --test-concurrency=1 <same two files>tests 43 / pass 43 / fail 0
rootbun test --timeout=300000 <same two files>43 pass / 0 fail
rootbun test --timeout=300000 -t "keeps a credential out of" packages/core/tests/guarded-journal.test.ts2 pass / 5 filtered out / 0 fail / 18 expect() calls
site/deno task checkexit 0
site/deno task build✓ built in 1.38s
rootdeno task lintexit 0, 0 errors
rootdeno task checkexit 0
rootdeno task check:jsrSuccess Dry run complete

The fourth row is not redundant. bun test prints only counts, so a suite that
silently skipped the two new cases would also report 43 pass; the filtered run
is what shows they ran under Bun and that their assertions executed.

Scope

Included

  • The /docs/journal rewrite and the /docs/reference CLI option entry.
  • Two default-scanner regressions, one per missing backend.

Intentionally unchanged

  • No production code. No scanner rule, finding shape, policy authority,
    durable event shape, output bridging, settlement, replay, or persistence
    adapter is touched.
  • No spec or architecture change.specs/executable-mdx-spec.md §8.1
    already states this contract; the website now matches it.
  • No CI change. The compiled-binary default/opt-out smoke in ci.yml stays
    as it is and remains the authority for that surface.
  • The memory permutations are not duplicated.secret-detection.test.ts
    already proves diagnostics, output withholding, settlement, replay and the
    disabled control. Those are execution-owned behavior, not backend variants, so
    each backend gets one focused case.
  • No runtime exclusion added. Both files stay out of
    scripts/runtime-test-exclusions.ts and run under all three runtimes.
  • The docs nav still reads "Journal gates". The page now leads with the
    built-in policy, so the label under-describes it, but renaming would touch
    site/routes/docs/_layout.tsx and site/routes/docs/agents.tsx — out of scope
    here, and flagged rather than done quietly.

Risks and limitations

  • The website is prose, so its accuracy is a reading rather than a test. Both
    pages were rendered from the built site and read as an operator would see them;
    every claim traces to specs/executable-mdx-spec.md §8.1 or to
    packages/cli/src/cli.ts, including the warning text quoted verbatim.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

…ch backend
The journal-gates page still described secret detection as something a host
brings: a `containsCredential` placeholder inside `guardDurableStream`. Every
execution has scanned by default since #199, so the page taught the wrong
common path, and the CLI reference did not mention the opt-out at all.
The page now leads with the built-in policy — default-on offline scanning
installed before the first live event, what a finding does to the offending
append and to the run, output withholding, the diagnostic that names a rule
and never a value, and replay that does not rescan. `secretDetection: false`
and `--no-secret-detection` appear as a dangerous diagnostic escape hatch with
the warning the CLI actually writes, not as remediation. `guardDurableStream`
keeps its gate-ordering, per-event and replay explanation, now as the generic
decorator it is, and its example refuses an oversized event rather than
deciding what a credential is. The reference lists the option with the same
invocation-wide semantics.
Two regressions cover the real default scanner over the two backends the
memory cases could not reach. Each puts a synthetic, run-time-assembled
credential in the root source, passes the backend straight to `execute()`
with default options, and asserts `Err(SecretDetectedError)`, the absence of
the rejected import, an independently safe `Close(err)`, and the canary
nowhere in the persisted events or the error's readable fields — through the
JSONL bytes on disk for the file backend, and through a read-back for HTTP.
@github-actions

Copy link
Copy Markdown

PR #575: 📄 Say that XMD scans for credentials by default, and prove it over each backend

3 files, +215 / -24

Scope

✅ PR scope looks good.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 24, 2026 13:58
@taras
taras merged commit 042e240 into mainAug 24, 2026
28 checks passed
@taras
taras deleted the agent/issue-199-final branch August 24, 2026 14:07
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
Sign up for freeto 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.

Reject secrets before journal persistence by default

1 participant

@taras
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

📄 Say that XMD scans for credentials by default, and prove it over each backend - #575

Merged
taras merged 1 commit into
mainfrom
agent/issue-199-final
Aug 24, 2026
Merged

📄 Say that XMD scans for credentials by default, and prove it over each backend#575
taras merged 1 commit into
mainfrom
agent/issue-199-final

Conversation

@taras

Copy link
Copy Markdown
Owner

Why

Closes#199. Secret detection has been on by default since slice 2, but the
website still taught it as something a host brings, and the CLI reference did
not mention the opt-out at all. Anyone reading the docs to decide whether XMD
protects their journal came away with the wrong answer.

The test corpus had the matching gap: the real default scanner was only ever
proven over InMemoryStream. Every file and HTTP case installed a
test-supplied gate instead, so nothing showed that execute()'s own scanner
reaches those two backends.

What changes

Before:/docs/journal opened with guardDurableStream and a
caller-supplied containsCredential placeholder, and told the reader that
"deciding what counts as a secret is your call, not this library's."
/docs/reference listed five CLI options and no way to turn detection off.

After:/docs/journal opens with the built-in policy and states the
observable contract — default-on offline scanning installed before the first
live event; what a finding does to the offending append and to the run; output
withholding; diagnostics that name a rule and never a value; replay that does
not rescan; and the host-only opt-out as a dangerous diagnostic escape hatch.
guardDurableStream follows it as the generic decorator it is.
/docs/reference lists --no-secret-detection with the same invocation-wide
semantics.

No production code changed. Behavior is exactly what shipped in #329, #330 and
#573.

Review guide

Start with:site/routes/docs/journal.tsx

Then review:

  1. site/routes/docs/reference.tsx — the new CLI option entry
  2. packages/core/tests/guarded-journal.test.ts — the new
    describe("the default scanner over a persistence backend") block

Look carefully at:

  • The two new tests pass the backend directly to execute(). If either one
    grows a guardDurableStream wrapper, it stops testing the thing it exists to
    test.
  • Both assert an independently safe Close(err) rather than an empty backend.

What must stay true

  • The website describes the default, not a custom gate. Checked by reading
    the rendered page; site/deno task check and deno task build keep it
    compiling and formatted.
  • --no-secret-detection stays the only spelling that disables detection.
    Enforced by packages/cli/src/cli.ts and checked by
    packages/cli/tests/secret-detection-cli.test.ts; the docs now state it.
  • A rejection never implies an empty journal. Both new tests assert the
    offending event is absent and that close(root) with status err is
    present.
  • No usable credential enters the repository. The canary is assembled at run
    time from an alphabet slice; nothing reads an environment variable, Git
    credential, or user configuration.

How to verify it

  • keeps a credential out of a file backend proves the real default scanner
    rejects a credential before file persistence, and sweeps the JSONL bytes on
    disk. It fails if the scanner is not installed, if the rejected import reaches
    the backend, or if a partial record is written.
  • keeps a credential out of an HTTP backend proves the same before HTTP
    persistence, reading back through the stream rather than anything the run
    retained.
  • Both assert the error's message and stack are canary-free, so a diagnostic
    that quoted the matched value would fail them.

Mutation evidence. On a throwaway copy of the file with the two new
execute() calls changed to secretDetection: false and nothing else touched,
both new cases failed at expect(result.ok).toBe(false) while the five existing
cases still passed. They discriminate the default rather than an incidental
refusal.

Commands run at 74ef1d7d, after one deno task setup in a fresh worktree:

Working directoryCommandResult
rootdeno task test packages/core/tests/secret-detection.test.ts packages/core/tests/guarded-journal.test.tsok | 3 passed (51 steps) | 0 failed
rootpnpm exec tsx --tsconfig tsconfig.node.json --test --test-concurrency=1 <same two files>tests 43 / pass 43 / fail 0
rootbun test --timeout=300000 <same two files>43 pass / 0 fail
rootbun test --timeout=300000 -t "keeps a credential out of" packages/core/tests/guarded-journal.test.ts2 pass / 5 filtered out / 0 fail / 18 expect() calls
site/deno task checkexit 0
site/deno task build✓ built in 1.38s
rootdeno task lintexit 0, 0 errors
rootdeno task checkexit 0
rootdeno task check:jsrSuccess Dry run complete

The fourth row is not redundant. bun test prints only counts, so a suite that
silently skipped the two new cases would also report 43 pass; the filtered run
is what shows they ran under Bun and that their assertions executed.

Scope

Included

  • The /docs/journal rewrite and the /docs/reference CLI option entry.
  • Two default-scanner regressions, one per missing backend.

Intentionally unchanged

  • No production code. No scanner rule, finding shape, policy authority,
    durable event shape, output bridging, settlement, replay, or persistence
    adapter is touched.
  • No spec or architecture change.specs/executable-mdx-spec.md §8.1
    already states this contract; the website now matches it.
  • No CI change. The compiled-binary default/opt-out smoke in ci.yml stays
    as it is and remains the authority for that surface.
  • The memory permutations are not duplicated.secret-detection.test.ts
    already proves diagnostics, output withholding, settlement, replay and the
    disabled control. Those are execution-owned behavior, not backend variants, so
    each backend gets one focused case.
  • No runtime exclusion added. Both files stay out of
    scripts/runtime-test-exclusions.ts and run under all three runtimes.
  • The docs nav still reads "Journal gates". The page now leads with the
    built-in policy, so the label under-describes it, but renaming would touch
    site/routes/docs/_layout.tsx and site/routes/docs/agents.tsx — out of scope
    here, and flagged rather than done quietly.

Risks and limitations

  • The website is prose, so its accuracy is a reading rather than a test. Both
    pages were rendered from the built site and read as an operator would see them;
    every claim traces to specs/executable-mdx-spec.md §8.1 or to
    packages/cli/src/cli.ts, including the warning text quoted verbatim.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

…ch backend
The journal-gates page still described secret detection as something a host
brings: a `containsCredential` placeholder inside `guardDurableStream`. Every
execution has scanned by default since #199, so the page taught the wrong
common path, and the CLI reference did not mention the opt-out at all.
The page now leads with the built-in policy — default-on offline scanning
installed before the first live event, what a finding does to the offending
append and to the run, output withholding, the diagnostic that names a rule
and never a value, and replay that does not rescan. `secretDetection: false`
and `--no-secret-detection` appear as a dangerous diagnostic escape hatch with
the warning the CLI actually writes, not as remediation. `guardDurableStream`
keeps its gate-ordering, per-event and replay explanation, now as the generic
decorator it is, and its example refuses an oversized event rather than
deciding what a credential is. The reference lists the option with the same
invocation-wide semantics.
Two regressions cover the real default scanner over the two backends the
memory cases could not reach. Each puts a synthetic, run-time-assembled
credential in the root source, passes the backend straight to `execute()`
with default options, and asserts `Err(SecretDetectedError)`, the absence of
the rejected import, an independently safe `Close(err)`, and the canary
nowhere in the persisted events or the error's readable fields — through the
JSONL bytes on disk for the file backend, and through a read-back for HTTP.
@github-actions

Copy link
Copy Markdown

PR #575: 📄 Say that XMD scans for credentials by default, and prove it over each backend

3 files, +215 / -24

Scope

✅ PR scope looks good.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 24, 2026 13:58
@taras
taras merged commit 042e240 into mainAug 24, 2026
28 checks passed
@taras
taras deleted the agent/issue-199-final branch August 24, 2026 14:07
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
Sign up for freeto 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.

Reject secrets before journal persistence by default

1 participant

@taras
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

📄 Say that XMD scans for credentials by default, and prove it over each backend - #575

Merged
taras merged 1 commit into
mainfrom
agent/issue-199-final
Aug 24, 2026
Merged

📄 Say that XMD scans for credentials by default, and prove it over each backend#575
taras merged 1 commit into
mainfrom
agent/issue-199-final

Conversation

@taras

Copy link
Copy Markdown
Owner

Why

Closes#199. Secret detection has been on by default since slice 2, but the
website still taught it as something a host brings, and the CLI reference did
not mention the opt-out at all. Anyone reading the docs to decide whether XMD
protects their journal came away with the wrong answer.

The test corpus had the matching gap: the real default scanner was only ever
proven over InMemoryStream. Every file and HTTP case installed a
test-supplied gate instead, so nothing showed that execute()'s own scanner
reaches those two backends.

What changes

Before:/docs/journal opened with guardDurableStream and a
caller-supplied containsCredential placeholder, and told the reader that
"deciding what counts as a secret is your call, not this library's."
/docs/reference listed five CLI options and no way to turn detection off.

After:/docs/journal opens with the built-in policy and states the
observable contract — default-on offline scanning installed before the first
live event; what a finding does to the offending append and to the run; output
withholding; diagnostics that name a rule and never a value; replay that does
not rescan; and the host-only opt-out as a dangerous diagnostic escape hatch.
guardDurableStream follows it as the generic decorator it is.
/docs/reference lists --no-secret-detection with the same invocation-wide
semantics.

No production code changed. Behavior is exactly what shipped in #329, #330 and
#573.

Review guide

Start with:site/routes/docs/journal.tsx

Then review:

  1. site/routes/docs/reference.tsx — the new CLI option entry
  2. packages/core/tests/guarded-journal.test.ts — the new
    describe("the default scanner over a persistence backend") block

Look carefully at:

  • The two new tests pass the backend directly to execute(). If either one
    grows a guardDurableStream wrapper, it stops testing the thing it exists to
    test.
  • Both assert an independently safe Close(err) rather than an empty backend.

What must stay true

  • The website describes the default, not a custom gate. Checked by reading
    the rendered page; site/deno task check and deno task build keep it
    compiling and formatted.
  • --no-secret-detection stays the only spelling that disables detection.
    Enforced by packages/cli/src/cli.ts and checked by
    packages/cli/tests/secret-detection-cli.test.ts; the docs now state it.
  • A rejection never implies an empty journal. Both new tests assert the
    offending event is absent and that close(root) with status err is
    present.
  • No usable credential enters the repository. The canary is assembled at run
    time from an alphabet slice; nothing reads an environment variable, Git
    credential, or user configuration.

How to verify it

  • keeps a credential out of a file backend proves the real default scanner
    rejects a credential before file persistence, and sweeps the JSONL bytes on
    disk. It fails if the scanner is not installed, if the rejected import reaches
    the backend, or if a partial record is written.
  • keeps a credential out of an HTTP backend proves the same before HTTP
    persistence, reading back through the stream rather than anything the run
    retained.
  • Both assert the error's message and stack are canary-free, so a diagnostic
    that quoted the matched value would fail them.

Mutation evidence. On a throwaway copy of the file with the two new
execute() calls changed to secretDetection: false and nothing else touched,
both new cases failed at expect(result.ok).toBe(false) while the five existing
cases still passed. They discriminate the default rather than an incidental
refusal.

Commands run at 74ef1d7d, after one deno task setup in a fresh worktree:

Working directoryCommandResult
rootdeno task test packages/core/tests/secret-detection.test.ts packages/core/tests/guarded-journal.test.tsok | 3 passed (51 steps) | 0 failed
rootpnpm exec tsx --tsconfig tsconfig.node.json --test --test-concurrency=1 <same two files>tests 43 / pass 43 / fail 0
rootbun test --timeout=300000 <same two files>43 pass / 0 fail
rootbun test --timeout=300000 -t "keeps a credential out of" packages/core/tests/guarded-journal.test.ts2 pass / 5 filtered out / 0 fail / 18 expect() calls
site/deno task checkexit 0
site/deno task build✓ built in 1.38s
rootdeno task lintexit 0, 0 errors
rootdeno task checkexit 0
rootdeno task check:jsrSuccess Dry run complete

The fourth row is not redundant. bun test prints only counts, so a suite that
silently skipped the two new cases would also report 43 pass; the filtered run
is what shows they ran under Bun and that their assertions executed.

Scope

Included

  • The /docs/journal rewrite and the /docs/reference CLI option entry.
  • Two default-scanner regressions, one per missing backend.

Intentionally unchanged

  • No production code. No scanner rule, finding shape, policy authority,
    durable event shape, output bridging, settlement, replay, or persistence
    adapter is touched.
  • No spec or architecture change.specs/executable-mdx-spec.md §8.1
    already states this contract; the website now matches it.
  • No CI change. The compiled-binary default/opt-out smoke in ci.yml stays
    as it is and remains the authority for that surface.
  • The memory permutations are not duplicated.secret-detection.test.ts
    already proves diagnostics, output withholding, settlement, replay and the
    disabled control. Those are execution-owned behavior, not backend variants, so
    each backend gets one focused case.
  • No runtime exclusion added. Both files stay out of
    scripts/runtime-test-exclusions.ts and run under all three runtimes.
  • The docs nav still reads "Journal gates". The page now leads with the
    built-in policy, so the label under-describes it, but renaming would touch
    site/routes/docs/_layout.tsx and site/routes/docs/agents.tsx — out of scope
    here, and flagged rather than done quietly.

Risks and limitations

  • The website is prose, so its accuracy is a reading rather than a test. Both
    pages were rendered from the built site and read as an operator would see them;
    every claim traces to specs/executable-mdx-spec.md §8.1 or to
    packages/cli/src/cli.ts, including the warning text quoted verbatim.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

…ch backend
The journal-gates page still described secret detection as something a host
brings: a `containsCredential` placeholder inside `guardDurableStream`. Every
execution has scanned by default since #199, so the page taught the wrong
common path, and the CLI reference did not mention the opt-out at all.
The page now leads with the built-in policy — default-on offline scanning
installed before the first live event, what a finding does to the offending
append and to the run, output withholding, the diagnostic that names a rule
and never a value, and replay that does not rescan. `secretDetection: false`
and `--no-secret-detection` appear as a dangerous diagnostic escape hatch with
the warning the CLI actually writes, not as remediation. `guardDurableStream`
keeps its gate-ordering, per-event and replay explanation, now as the generic
decorator it is, and its example refuses an oversized event rather than
deciding what a credential is. The reference lists the option with the same
invocation-wide semantics.
Two regressions cover the real default scanner over the two backends the
memory cases could not reach. Each puts a synthetic, run-time-assembled
credential in the root source, passes the backend straight to `execute()`
with default options, and asserts `Err(SecretDetectedError)`, the absence of
the rejected import, an independently safe `Close(err)`, and the canary
nowhere in the persisted events or the error's readable fields — through the
JSONL bytes on disk for the file backend, and through a read-back for HTTP.
@github-actions

Copy link
Copy Markdown

PR #575: 📄 Say that XMD scans for credentials by default, and prove it over each backend

3 files, +215 / -24

Scope

✅ PR scope looks good.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 24, 2026 13:58
@taras
taras merged commit 042e240 into mainAug 24, 2026
28 checks passed
@taras
taras deleted the agent/issue-199-final branch August 24, 2026 14:07
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
Sign up for freeto 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.

Reject secrets before journal persistence by default

1 participant

@taras
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

📄 Say that XMD scans for credentials by default, and prove it over each backend - #575

Merged
taras merged 1 commit into
mainfrom
agent/issue-199-final
Aug 24, 2026
Merged

📄 Say that XMD scans for credentials by default, and prove it over each backend#575
taras merged 1 commit into
mainfrom
agent/issue-199-final

Conversation

@taras

Copy link
Copy Markdown
Owner

Why

Closes#199. Secret detection has been on by default since slice 2, but the
website still taught it as something a host brings, and the CLI reference did
not mention the opt-out at all. Anyone reading the docs to decide whether XMD
protects their journal came away with the wrong answer.

The test corpus had the matching gap: the real default scanner was only ever
proven over InMemoryStream. Every file and HTTP case installed a
test-supplied gate instead, so nothing showed that execute()'s own scanner
reaches those two backends.

What changes

Before:/docs/journal opened with guardDurableStream and a
caller-supplied containsCredential placeholder, and told the reader that
"deciding what counts as a secret is your call, not this library's."
/docs/reference listed five CLI options and no way to turn detection off.

After:/docs/journal opens with the built-in policy and states the
observable contract — default-on offline scanning installed before the first
live event; what a finding does to the offending append and to the run; output
withholding; diagnostics that name a rule and never a value; replay that does
not rescan; and the host-only opt-out as a dangerous diagnostic escape hatch.
guardDurableStream follows it as the generic decorator it is.
/docs/reference lists --no-secret-detection with the same invocation-wide
semantics.

No production code changed. Behavior is exactly what shipped in #329, #330 and
#573.

Review guide

Start with:site/routes/docs/journal.tsx

Then review:

  1. site/routes/docs/reference.tsx — the new CLI option entry
  2. packages/core/tests/guarded-journal.test.ts — the new
    describe("the default scanner over a persistence backend") block

Look carefully at:

  • The two new tests pass the backend directly to execute(). If either one
    grows a guardDurableStream wrapper, it stops testing the thing it exists to
    test.
  • Both assert an independently safe Close(err) rather than an empty backend.

What must stay true

  • The website describes the default, not a custom gate. Checked by reading
    the rendered page; site/deno task check and deno task build keep it
    compiling and formatted.
  • --no-secret-detection stays the only spelling that disables detection.
    Enforced by packages/cli/src/cli.ts and checked by
    packages/cli/tests/secret-detection-cli.test.ts; the docs now state it.
  • A rejection never implies an empty journal. Both new tests assert the
    offending event is absent and that close(root) with status err is
    present.
  • No usable credential enters the repository. The canary is assembled at run
    time from an alphabet slice; nothing reads an environment variable, Git
    credential, or user configuration.

How to verify it

  • keeps a credential out of a file backend proves the real default scanner
    rejects a credential before file persistence, and sweeps the JSONL bytes on
    disk. It fails if the scanner is not installed, if the rejected import reaches
    the backend, or if a partial record is written.
  • keeps a credential out of an HTTP backend proves the same before HTTP
    persistence, reading back through the stream rather than anything the run
    retained.
  • Both assert the error's message and stack are canary-free, so a diagnostic
    that quoted the matched value would fail them.

Mutation evidence. On a throwaway copy of the file with the two new
execute() calls changed to secretDetection: false and nothing else touched,
both new cases failed at expect(result.ok).toBe(false) while the five existing
cases still passed. They discriminate the default rather than an incidental
refusal.

Commands run at 74ef1d7d, after one deno task setup in a fresh worktree:

Working directoryCommandResult
rootdeno task test packages/core/tests/secret-detection.test.ts packages/core/tests/guarded-journal.test.tsok | 3 passed (51 steps) | 0 failed
rootpnpm exec tsx --tsconfig tsconfig.node.json --test --test-concurrency=1 <same two files>tests 43 / pass 43 / fail 0
rootbun test --timeout=300000 <same two files>43 pass / 0 fail
rootbun test --timeout=300000 -t "keeps a credential out of" packages/core/tests/guarded-journal.test.ts2 pass / 5 filtered out / 0 fail / 18 expect() calls
site/deno task checkexit 0
site/deno task build✓ built in 1.38s
rootdeno task lintexit 0, 0 errors
rootdeno task checkexit 0
rootdeno task check:jsrSuccess Dry run complete

The fourth row is not redundant. bun test prints only counts, so a suite that
silently skipped the two new cases would also report 43 pass; the filtered run
is what shows they ran under Bun and that their assertions executed.

Scope

Included

  • The /docs/journal rewrite and the /docs/reference CLI option entry.
  • Two default-scanner regressions, one per missing backend.

Intentionally unchanged

  • No production code. No scanner rule, finding shape, policy authority,
    durable event shape, output bridging, settlement, replay, or persistence
    adapter is touched.
  • No spec or architecture change.specs/executable-mdx-spec.md §8.1
    already states this contract; the website now matches it.
  • No CI change. The compiled-binary default/opt-out smoke in ci.yml stays
    as it is and remains the authority for that surface.
  • The memory permutations are not duplicated.secret-detection.test.ts
    already proves diagnostics, output withholding, settlement, replay and the
    disabled control. Those are execution-owned behavior, not backend variants, so
    each backend gets one focused case.
  • No runtime exclusion added. Both files stay out of
    scripts/runtime-test-exclusions.ts and run under all three runtimes.
  • The docs nav still reads "Journal gates". The page now leads with the
    built-in policy, so the label under-describes it, but renaming would touch
    site/routes/docs/_layout.tsx and site/routes/docs/agents.tsx — out of scope
    here, and flagged rather than done quietly.

Risks and limitations

  • The website is prose, so its accuracy is a reading rather than a test. Both
    pages were rendered from the built site and read as an operator would see them;
    every claim traces to specs/executable-mdx-spec.md §8.1 or to
    packages/cli/src/cli.ts, including the warning text quoted verbatim.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

…ch backend
The journal-gates page still described secret detection as something a host
brings: a `containsCredential` placeholder inside `guardDurableStream`. Every
execution has scanned by default since #199, so the page taught the wrong
common path, and the CLI reference did not mention the opt-out at all.
The page now leads with the built-in policy — default-on offline scanning
installed before the first live event, what a finding does to the offending
append and to the run, output withholding, the diagnostic that names a rule
and never a value, and replay that does not rescan. `secretDetection: false`
and `--no-secret-detection` appear as a dangerous diagnostic escape hatch with
the warning the CLI actually writes, not as remediation. `guardDurableStream`
keeps its gate-ordering, per-event and replay explanation, now as the generic
decorator it is, and its example refuses an oversized event rather than
deciding what a credential is. The reference lists the option with the same
invocation-wide semantics.
Two regressions cover the real default scanner over the two backends the
memory cases could not reach. Each puts a synthetic, run-time-assembled
credential in the root source, passes the backend straight to `execute()`
with default options, and asserts `Err(SecretDetectedError)`, the absence of
the rejected import, an independently safe `Close(err)`, and the canary
nowhere in the persisted events or the error's readable fields — through the
JSONL bytes on disk for the file backend, and through a read-back for HTTP.
@github-actions

Copy link
Copy Markdown

PR #575: 📄 Say that XMD scans for credentials by default, and prove it over each backend

3 files, +215 / -24

Scope

✅ PR scope looks good.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 24, 2026 13:58
@taras
taras merged commit 042e240 into mainAug 24, 2026
28 checks passed
@taras
taras deleted the agent/issue-199-final branch August 24, 2026 14:07
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 25, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
taras added a commit that referenced this pull request Aug 26, 2026
Six exact claims outlived the capabilities that disproved them, and one of them
was damage I introduced while fixing another.
`<Fetch>` no longer owns the pull-request evidence: the specification said "that
read is `<Fetch>`'s" and "no stage writes those fetches yet" three sections away
from the stage that now writes all three URL-addressed reads. Default-on secret
rejection was still listed open in the primitives matrix after #573 and #575
shipped it. And one sentence read "That gate is and that gate is default-on" —
a duplicated fragment from an earlier edit, which is exactly the kind of repair
that should be made in place rather than appended later.
The larger correction is what #301 slice 2 *is*. Four passages called it
"scheduling the loop and continuing it unattended", which is the opposite of the
boundary: slice 2 is the composition — the authored loop from discovery through
acceptance under one run — and scheduling, watchers and unattended resume are
deliberately outside it, with the later scheduling slice belonging to #300. Two
more presented running the stages by hand under `xmd run` as the current
execution path; it is history, and the composed root runs under `xmd workflow
start`.
The sweeps missed every one of these while exiting 0, which is the real defect.
They now carry the nine exact defective sentences from the previous head as
forbidden forms, self-tested, and fail against that revision while passing here.
A scan for repeated phrases guards the duplication class directly, since a
sweep that only knows the claims it was taught cannot see prose damage.
The planning record is collapsed to match: the A/B/C sequence is gone from the
composition plan, the implementor handoff and issue #301. A shipped in #577, B
shipped in #580, and PR #181 owns C. The twelve-criterion matrix is unchanged —
only its ownership column moves, with criteria 2 and 2a inherited from #577 and
criterion 6's provider and replay halves from #580.
Sign up for freeto 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.

Reject secrets before journal persistence by default

1 participant

@taras