Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
893c43f
chore: capture two trufflehog findings met while routing tool checks
REPPL Sep 26, 2026
0289455
feat(tools): the tool registry and the explain-then-install mode
REPPL Sep 26, 2026
1e36dec
feat(record): the canonical term-overlap primitive for matching at fi…
REPPL Sep 26, 2026
b3690f0
feat(record): match.threshold and match.fields through the layered re…
REPPL Sep 26, 2026
edef568
feat: explain a missing gitleaks or gh in the refusal itself
REPPL Sep 26, 2026
920afe8
feat(ahoy): explain each missing tool and install it only on a yes
REPPL Sep 26, 2026
d2a71c7
feat(ahoy): a named tool answers the dependency question
REPPL Sep 26, 2026
22e6edb
test(cli): the named-tool yes holds with nothing typed
REPPL Sep 26, 2026
90aa2e8
chore: resolve iss-2609261447331434 — ahoy stops offering trufflehog
REPPL Sep 26, 2026
d3f1f59
feat(capture,intent): match a new capture or draft against the record…
REPPL Sep 26, 2026
bea449b
chore: close spc-2609211955339422 and ship itd-63
REPPL Sep 26, 2026
fc9ceb1
style: sort the history store's imports
REPPL Sep 26, 2026
21db956
feat(cli): capture and the intent create print the filing-time match
REPPL Sep 26, 2026
aafd4f2
docs: the filing-time match on the capture and intent surfaces, and t…
REPPL Sep 26, 2026
883e394
docs(acknowledgements): inverse document frequency, for the filing-ti…
REPPL Sep 26, 2026
63703c6
chore(spec): close spc-2609212141417782, ship itd-2609212137116617
REPPL Sep 26, 2026
934a493
docs(intents): qualify predecessor spec ids and retract the itd-44 de…
REPPL Sep 26, 2026
ea370c1
chore: capture iss-2609261536147903 — planned intents cite predecesso…
REPPL Sep 26, 2026
522e72c
chore: resolve iss-239 — predecessor spec ids qualified in the draft …
REPPL Sep 26, 2026
b2fc7f3
docs(release-gate): the manifest claims the inputs it pins, not compa…
REPPL Sep 26, 2026
1497146
chore: resolve iss-2608231409595789 — the manifest claims pinned inpu…
REPPL Sep 26, 2026
fce93af
fix(reading): the raw heading scan is linear in the document
REPPL Sep 26, 2026
bca1227
chore: resolve iss-2608301421382564 — the raw heading scan is linear
REPPL Sep 26, 2026
473bfa4
chore: capture the four review-itd63 findings on the tool installer
REPPL Sep 26, 2026
cce6eb9
fix(grounds): refuse grounds text the site renderer refuses
REPPL Sep 26, 2026
4220cb7
chore: resolve iss-2608301646046226 — grounds text must render
REPPL Sep 26, 2026
705b821
fix(tools): bound the install wait against a re-grouped descendant
REPPL Sep 26, 2026
84f5d0e
chore: resolve iss-2609261604485703 — the install bound holds against…
REPPL Sep 26, 2026
291410b
fix(tools): pass a program's output through termsafe before it enters…
REPPL Sep 26, 2026
28a371c
chore: resolve iss-2609261604498137 — a program's output is sanitised…
REPPL Sep 26, 2026
09938d6
fix(tools): say what a Homebrew install sends before the person consents
REPPL Sep 26, 2026
ce4862b
chore: resolve iss-2609261604492132 — the install Effects text says w…
REPPL Sep 26, 2026
7f34670
refactor: one CI detector for the load check and the tool installer
REPPL Sep 26, 2026
6b9495f
chore: resolve iss-2609261604499090 — one CI detector serves the load…
REPPL Sep 26, 2026
28d7b3e
test(mdrecord): the fence allowlist follows the renderer to core/mdre…
REPPL Sep 26, 2026
a1c5dfc
test(reading): assert the linearity bound in the plain lane only
REPPL Sep 26, 2026
77b1d3f
chore(issues): capture the three review-match follow-ups
REPPL Sep 26, 2026
b24ec86
fix(capture): an unreadable status directory is unread, not empty
REPPL Sep 26, 2026
b4b1476
chore: resolve iss-2609261631120364 — an unreadable status directory …
REPPL Sep 26, 2026
bee225d
docs(spec): amend spc-2609212141417782 with what the match shipped as
REPPL Sep 26, 2026
18d118c
chore: resolve iss-2609261631134401 — the closed match spec names wha…
REPPL Sep 26, 2026
65cce5a
fix(lint): record_schema reports every issue the ledger reader skips
REPPL Sep 26, 2026
f5d7ea7
chore: resolve iss-2609261631132673 — record-lint reports every recor…
REPPL Sep 26, 2026
6639841
fix(frontmatter): read comments and nulls one way in every field reader
REPPL Sep 26, 2026
44fe822
chore: resolve iss-2608241347321759 — comment and null read one way
REPPL Sep 26, 2026
bc9fca9
fix(lint): record_schema names a grounds bullet the reader drops
REPPL Sep 26, 2026
f688ca0
chore: resolve iss-2608301747001641 — dropped grounds bullets are named
REPPL Sep 26, 2026
ed2264f
merge: land lane drainM1 (fix/drain-majors-1 a1c5dfc7): no textual co…
REPPL Sep 26, 2026
3a9017f
docs(mdrender): restore the lowercase prose "quote" the rename turned…
REPPL Sep 26, 2026
3bde241
merge: land lane itd63 (feat/tools-explain-install 6b9495f6): clean
REPPL Sep 26, 2026
cee142d
fix(capture): wontfix acts on a record whose body leaves an opener open
REPPL Sep 26, 2026
8b3a7ad
chore: resolve iss-2608301908270888 — no triage verb locks a record out
REPPL Sep 26, 2026
1babb04
chore: re-defer iss-2608310912217521 to v0.11.0 on the ruling it needs
REPPL Sep 26, 2026
11e93e5
merge: land lane match (feat/record-match-before-write f5d7ea71): thr…
REPPL Sep 26, 2026
fb63597
refactor(capture): point the parser tests at issuerecord and drop the…
REPPL Sep 26, 2026
17e6294
chore: capture iss-2609261726015043 — lint reads CHANGELOG.md out of …
REPPL Sep 26, 2026
9b44629
fix(lint): bound the release-gate's CHANGELOG read out of git, and le…
REPPL Sep 26, 2026
edcea15
chore: resolve iss-2609261726015043 — the release-gate's CHANGELOG re…
REPPL Sep 26, 2026
cacbc17
merge: bring main (#727, integration branch 3, 5735c89a) into the int…
REPPL Sep 26, 2026
b27d5aa
merge: land lane drainR1 (fix/drain-record-reading 1babb041): one con…
REPPL Sep 26, 2026
5af3f87
fix(capture): wontfix refuses a body that leaves an opener open again
REPPL Sep 26, 2026
3464d95
chore: recalibrate the reading windows at the integration tip
REPPL Sep 26, 2026
404fd99
chore: wontfix iss-2608301908270888 — the refusal is the ruled answer
REPPL Sep 26, 2026
fa26c55
merge: bring drainR1's ruling alignment (fix2-drainR1, 404fd996) into…
REPPL Sep 26, 2026
00a2506
merge: bring main (#728, #729, #730) into the integration branch
REPPL Sep 28, 2026
8d12627
chore: recalibrate the reading windows at the re-merged integration tip
REPPL Sep 28, 2026
be55bf0
test(cli): clear GITHUB_ACTIONS as well as CI in the tool-confirmatio…
REPPL Sep 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions .abcd/config/reading-presets.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@
"test"
],
"window": {
"tokens_est": 1280000,
"measured_tokens_est": 1265015,
"measured_bytes": 4870311,
"measured_at": "b198acca0dd490d7e48fb0d11c54382fa826c21a"
"tokens_est": 1300000,
"measured_tokens_est": 1277632,
"measured_bytes": 4918884,
"measured_at": "00a250663e9d9f4631f7e7e0e2485f67b75f961a"
}
},
"entailment": {
Expand Down Expand Up @@ -133,9 +133,9 @@
],
"window": {
"tokens_est": 390000,
"measured_tokens_est": 377255,
"measured_bytes": 1452432,
"measured_at": "b198acca0dd490d7e48fb0d11c54382fa826c21a"
"measured_tokens_est": 379456,
"measured_bytes": 1460909,
"measured_at": "00a250663e9d9f4631f7e7e0e2485f67b75f961a"
}
},
"comparative": {
Expand Down Expand Up @@ -216,10 +216,10 @@
"test"
],
"window": {
"tokens_est": 1290000,
"measured_tokens_est": 1274051,
"measured_bytes": 4905099,
"measured_at": "b198acca0dd490d7e48fb0d11c54382fa826c21a"
"tokens_est": 1300000,
"measured_tokens_est": 1286668,
"measured_bytes": 4953672,
"measured_at": "00a250663e9d9f4631f7e7e0e2485f67b75f961a"
}
}
}
Expand Down
31 changes: 27 additions & 4 deletions .abcd/development/brief/04-surfaces/01-ahoy.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ about, one question per category present, never one per item.
| `safe-autocreate` | the repo skeleton, history-store directories, the name-guard artefacts | applied once the category is approved, no per-item prompt; create-if-absent, never overwriting |
| `config-change` | visibility, oracle adapter, the `PATH` entry, the git-identity pin, the artefact kind | transparent confirm; skip-if-set with a "current value" notice |
| `plugin-owned` | the marker block (itd-3); hook-manifest verification | silent overwrite on marker drift; a non-resolvable diagnostic for a malformed or missing manifest, and for a conventions file whose block would land inside a fence or HTML comment nothing closes (`marker.unplaceable`) |
| `dependency` | the opt-in scanners | one category-level approval covering them; abcd never auto-executes a package manager, and the user runs the commands |
| `dependency` | a tool a capability uses and cannot find: gitleaks, optional over the native secret scanner and required where the repository armed it in `.abcd/config/gitleaks.json` | the category approval reaches the step; each tool is then explained from the tool registry (what it is, optional or required here, what works without it, the exact install step, what the install does) and its install step runs only on a per-tool yes — typed at a terminal, or relayed by a host as a flag naming the tool — never under the approve-everything flag, a piped answer or CI; a no is reported as what the capability continues on |
| `status-line` | the offer of abcd's status line in the host harness | an advisory offer asked after its own question, written only on an answered consent; never under the approve-everything flag, and reported as optional work it skipped |
| `oracle-routing` | the offer of abcd's proposed model-tier routing table (itd-2609170822093401): the machine's `~/.abcd/oracle-routing.json`, then, as a separate question, the repository's `.abcd/config/oracle-routing.json` | the proposal rendered as a table (agent, tier, fan-out) and each file written only on its own answered consent, the machine one owner-only; never under the approve-everything flag, and reported as optional work it skipped; a decline records nothing, so the next install offers again; uninstall leaves both files |
| `user-state` | the registry entry, re-founding, stale or duplicate entries | guided; never auto-edit user-scope state, report extras read-only |
Expand Down Expand Up @@ -448,6 +448,25 @@ prompt wait rather than decline, which is the contract every prompting CLI has.
A run that must neither block nor prompt closes stdin and pre-answers with
flags.

**Installing a tool is the one question a piped answer never answers.** It runs
a program on the machine, so it is asked only of a person at a terminal, after
the tool registry's explanation is shown, and its default is no. Off a terminal
the answer is a flag naming the tool, which is how a host relays the answer its
own question tool got; the approve-everything flag never installs a tool, and a
CI runner never installs one and is not asked: the canonical CI detector
(`internal/cienv`, `GITHUB_ACTIONS=true` or a truthy `CI`) refuses, and so does
`CI` set to any value, `false` and `0` included. What runs is the
registry's fixed argv for the platform, never a shell string and never a command
composed from input, and only when the package manager resolves on `PATH`
outside the repository (`internal/core/tools`). The step runs in a process group
of its own, bounded at 15 minutes (its verify at 30 seconds), and a timeout kills
that group through the handle abcd holds. The kill has one limit: a process the
step moves into another group or session (`setsid`, `setpgid`) is out of its
reach and can outlive the run. abcd stops reading output 10 seconds after the
step exits or is killed, so such a process holding the output open cannot hold
the run past its bound, and a step that exits cleanly while leaving one behind
is reported as failed, saying so.

The non-interactive flags pre-answer the prompts: approve every resolvable
category, decide the adoption question either way, set the marker target, the
oracle backend, the deep-scan toggle and the repo visibility, select track-latest
Expand Down Expand Up @@ -591,9 +610,12 @@ byte-identical to a fresh install save for the setup date.
- **Given** a repo with the install run at an older setup version, **when**
the install runs, **then** the version is updated, the marker block refreshed,
and existing config keys preserved.
- **Given** an opt-in scanner is not on `PATH`, **when** the dependency category
is approved, **then** the user is shown the install commands under one
category-level approval; abcd never auto-executes a package manager.
- **Given** a tool a capability uses is not on `PATH`, **when** the dependency
category is approved, **then** the person is shown the tool registry's
explanation and asked per tool; the registry's fixed install step runs only
on a yes typed at a terminal or relayed by a host naming the tool, the result
reports what ran and whether its verify passed, and a no reports what the
capability continues on (itd-63).
- **Given** no oracle adapter is wired, **when** detection resolves the oracle,
**then** it stays host-delegated: abcd needs no API keys or model config,
because it emits prompts the host runs (adr-25), and an adapter can be
Expand Down Expand Up @@ -665,6 +687,7 @@ Sub-verbs: none.
| `--bin-dir` | string |
| `--dev` | bool |
| `--docs-target` | string |
| `--install-tool` | stringSlice |
| `--oracle-backend` | string |
| `--refuse-adopt` | bool |
| `--scan-deep` | string |
Expand Down
8 changes: 7 additions & 1 deletion .abcd/development/brief/04-surfaces/05-intent.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ Later phase — intent-auditor (shape-classification role) scans the corpus
| Subcommand | Purpose | File movement |
|---|---|---|
| `/abcd:intent` (no args) | Read-only status: bucket counts (drafts / planned / shipped / disciplines / superseded) with the count of owed fidelity reviews beside them (the owed listing's total, from the same reader), open/closed spec counts, the itd↔spc links, and in the machine-readable form a per-intent listing (id, title, bucket, `ac_state` `real` or `seeded`, and the filing date a timestamp id encodes, null for an ordinal id; iss-242), a ledger-routing hint (`abcd capture "…"` for an observation, `abcd intent "…"` for a user-facing change), and an ideate-routing line (a big, unproven idea? `abcd ideate` runs the optional admission gauntlet and records the verdict either way) | — |
| `/abcd:intent "<free-text>"` | **Canonical create** (spc-30 (predecessor store)/itd-46): a leading quoted seed is the canonical create entry. Seeds a draft skeleton whose `## Press Release` is the quoted text as prose, under an H1 derived from the text's first sentence (cut on a word boundary at the slug cap) or given as a title — one line, non-empty, redacted like the text — with Why This Matters and Acceptance Criteria seeded as prompts for the human to fill; assigns `itd-N` and derives the slug from the text; writes `suggested_kind: null`. An optional impact (additive, breaking or fix) stamps the draft's product impact at create time, and an optional production mode (hand-written, dictated-and-formatted or scribe-transcribed) stamps how its text was produced (itd-178); the draft's `origin` carries no flag and is derived from the verb that ran. A leading quote always creates — never falls through to bare render | writes to `drafts/itd-N-<slug>.md` (no spec created) |
| `/abcd:intent "<free-text>"` | **Canonical create** (spc-30 (predecessor store)/itd-46): a leading quoted seed is the canonical create entry. Seeds a draft skeleton whose `## Press Release` is the quoted text as prose, under an H1 derived from the text's first sentence (cut on a word boundary at the slug cap) or given as a title — one line, non-empty, redacted like the text — with Why This Matters and Acceptance Criteria seeded as prompts for the human to fill; assigns `itd-N` and derives the slug from the text; writes `suggested_kind: null`. An optional impact (additive, breaking or fix) stamps the draft's product impact at create time, and an optional production mode (hand-written, dictated-and-formatted or scribe-transcribed) stamps how its text was produced (itd-178); the draft's `origin` carries no flag and is derived from the verb that ran. Before the draft is written, under the store's mint lock, its title and press release are matched against every open and resolved issue and every intent by the term-overlap heuristic `/abcd:capture` uses (itd-2609212137116617): a record at or above `match.threshold` is written onto the draft as `duplicates:` or `refines:`, at most three links, and the output lists the near misses below it with their scores; the match never refuses the create. A leading quote always creates — never falls through to bare render | writes to `drafts/itd-N-<slug>.md` (no spec created) |
| The grill step, on one intent id | Socratic adversarial interview that stress-tests an intent for vagueness, missing acceptance, hidden assumptions before planning. Glossary-aware once `terminology/` exists. A brief-section mode would stress-test a brief section instead. (per itd-27, `intents/planned/` — a later phase; no grill sub-verb ships yet) | (stays in current state) |
| Plan (one intent id) | Plans a draft: mints its native spec, injects the bidirectional link (intent `spec_id` ↔ spec `intent`), stamps an identity onto every unmarked scope condition, and moves the file `drafts/` → `planned/`. An impact given at planning stamps the INTENT's product-impact judgement, because the planning interview is where that judgement is made: validated at the create path's bar (never `internal`), written as the bare scalar the create path writes, refused before anything moves when it disagrees with a judgement the record already carries, and a no-op when it agrees; without one the field is left as found and the judgement stays owed to the close (iss-2609170726457256). A production mode given at planning stamps the MINTED SPEC's disclosure pair; the intent's own stamp was written at create time and is never rewritten. On an intent already in `planned/` it does the identity step alone (no spec, no move), takes an impact under the same rules, and refuses when nothing is unmarked and no judgement is added — except where the planned record's `spec_id` is null, when it mints (or reuses the spec already naming the intent) and links the spec in place on the draft's Acceptance Criteria bar, still with no move, and the readiness gate's remedy for the missing spec names this call (iss-2609211738504433). | `drafts/` → `planned/` (stamp step: no move) |
| Plan a bundle (several intent ids, a bundle name) | **The bundle command** (itd-34): plans two or more drafts as ONE shared spec. The name is the human's (the plugin page asks for it; the CLI refuses several ids without one, and a name with one id), kebab-case, and carried by no other record. It refuses a member naming another in `blocked_by`, naming the edge, and any member that is not a plannable draft — held, without criteria, already specced or naming another bundle — before the mint. It mints one spec whose frontmatter lists every member (`intents:` beside `intent:`, and `bundle:`), stamps `kind: bundle-member`, `bundle: <name>`, the scope-condition identities and an impact given at planning onto each member, links each `spec_id` to the shared spec, and moves all of them together; a failure after the mint puts every member back and takes the spec back. The shared spec's close ships every member together. | every member `drafts/` → `planned/` |
Expand Down Expand Up @@ -359,6 +359,12 @@ production_mode: hand-written # how the text was produced: hand-written | dictat
# draft, which graduated from nothing. The shipped
# record_provenance rule holds it against origin:
# extracted-from-record / contributed-by-reading
# duplicates: [iss-N | itd-N, ...] — written by the filing-time match (itd-2609212137116617)
# refines: [iss-N | itd-N, ...] when a record clears match.threshold: a near-identical
# double, or a broader record this draft is the narrower case of.
# A person confirms a link by leaving it and removes it by
# deleting the line; record_schema resolves both like every
# other cross-reference
# Added later, not part of the seed skeleton:
# held: "<reason>" — the hold the intent verb writes and its unhold removes, on a
# drafts/ or planned/ record only: one non-empty line, redacted before the
Expand Down
50 changes: 48 additions & 2 deletions .abcd/development/brief/04-surfaces/06-capture.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ refuses is counted in none of the three totals, so the board counts it beside
them and names, for each one, the reader layer that refused it: the filename,
the guarded read, the frontmatter parse, the schema or the folder and filename
invariants. The layer is what tells a reader whether the record or the reader is
the side to fix (iss-2609120452071388). The board also counts the records git
the side to fix (iss-2609120452071388). A status directory that exists and
cannot be listed is not counted as empty and is not an entry: the board faults
naming the directory, as the list, the transitions and the mint do
(iss-2609261241121312, iss-2609261631120364). The board also counts the records git
reports as untracked or changed and marks each such row: folder membership is a
status only once the file is committed, so an uncommitted record is in no state to
any other branch, worktree or gate (iss-2609100508570527).
Expand Down Expand Up @@ -81,6 +84,24 @@ record names no location in this checkout, so nothing ties it to the repository
it is filed into: that is a nudge, not a gate, and it is the shape every
misfiled record behind iss-2609120511058115 had (iss-2609231156260287).

Before the record is written, the fast path matches its text against the
record (itd-2609212137116617): every open and resolved issue's body and every
intent's title and press release, under the ledger lock, through the
term-overlap primitive in `internal/core/record/match`. The score is the share
of the new text's terms a candidate already holds, each term weighted by how
rare it is across the candidates, and it is declared a lexical heuristic on
every output. A candidate at or above `match.threshold` is written onto the
new record as `duplicates:` (the two hold each other's terms) or `refines:`
(the candidate holds this text's terms and more, so this record is the
narrower), at most three links; the output lists the rest, and the best five
below the threshold as near misses with their scores. The match never refuses
and never drops a capture: a text with fewer than eight distinct terms, a record
set that cannot be read and a configuration the reader refuses each file the
record unlinked, and the output says which. A person confirms a link by leaving
it and removes it by deleting its line, which leaves an ordinary record. The
match proposes no `reverses` and no `supersedes`: the itd-84 discipline keeps a
reversal advisory and human.

One flag belongs to one category: the lapse-instant flag carries the RFC 3339
instant a recorded discipline gave way, for the `lapse` category, and it has no
default.
Expand Down Expand Up @@ -339,6 +360,8 @@ related_specs: [spc-N, ...]
related_issues: [iss-N, ...]
synthesis_clusters: [<label>, ...] # optional synthesis grouping
blocked_by: [iss-N, ...] # dependency edges, written at capture or afterwards by linking; blocked/priority is derived, never stored
duplicates: [iss-N|itd-N, ...] # written by the filing-time match: a near-identical double; removed by hand when wrong
refines: [iss-N|itd-N, ...] # written by the filing-time match: this record is the narrower case
wontfix_reason: "<text>" # required when in wontfix/
resolution: "<one-line>" # required when in resolved/
shipped_in: vX.Y.Z # migration use: the release that already carried the work
Expand Down Expand Up @@ -398,7 +421,21 @@ token names. Appending rather than setting is the point: a later triage route
adds a bullet beside the one an earlier route recorded, and neither overwrites
the other. The section is held by `internal/core/grounds` per adr-57, and
`record_schema` blocks a frontmatter `grounds:` key by naming this section as
where the value belongs.
where the value belongs. It also blocks a top-level bullet in the section that
does not parse as `- <token>: <text>`, on an issue or an intent record: the
reader drops such a bullet, so a hand edit spelled `- rejected: …` would
otherwise be no recorded ground to any surface while the record reads as
carrying one. Which bullets the reader drops is `internal/core/grounds`'s own
answer, so the gate and the reader cannot disagree about it, and a paragraph of
prose under the heading is not a bullet and is not judged.

A body that leaves an HTML comment or a fence open is refused on every route
that would append a bullet, a wontfix included, exit 2 and nothing written: the
opener masks every line below it, so the bullet could not be read back. The
refusal names the construct and its body line, and the repair is the hand edit:
close or remove the opener in a text editor, then re-run the verb
(iss-2608301908270888). A resolve or a promote given no grounds appends nothing,
so it acts.

**The grounds text is gated on substance, not only on grammar.** A value that
parses as `<token>: <text>` is still refused, exit 2 and nothing written, unless
Expand All @@ -411,6 +448,15 @@ cases; a floor set high enough to judge reasoning would only buy padding. In a
script written without inter-word spaces each letter counts as one unit, so the
word floor does not fall on the writer of a Chinese or Japanese text.

**The grounds text must render.** The section is append-only and no verb removes
an entry, so a text the site's renderer refuses — an unclosed code span, an
image, raw HTML, a reference link, a link title — is refused before anything is
written, exit 2, on every route that records grounds, the reason-derived
`declined:` entry of a wontfix included. The question is put to the renderer
itself (`internal/core/mdrender`), so what is refused is exactly what a site
build refuses, judged strictly where a page would supply context: every
reference link and every image is refused.

## 4. Legacy scratch migration

**Not built yet.** The migration rides the `dev-sync work` surface
Expand Down
Loading
Loading