v0.5.0 — the product implements a published Markdown specification - #74
Merged
Conversation
`kindOf` answered `list` only when an entry held more than one value, so `tags: [contracts]` was an enum and `tags: [contracts, budget]` was a list. One attribute therefore had two kinds across the notes of a single vault, decided by a fact about whichever note was being read, and adding a second value silently changed what the attribute was. The frontmatter reader now carries the written form out alongside the values, and the kind follows the form: a bracketed or dash list is a list at any length, a scalar is an enum. RN-DSC-020 already said the classification is by the shape of the value; this is the code catching up with it, so no rule is created. The reading surface already drew a list of one as a list, through a different reader it may not share. It is now pinned to the same cases, so the two agree by rule rather than by coincidence. Closes #70 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
Three defects composed into one dead end: the screen sat on "Loading..." forever, a reload changed nothing, and signing out was the only way through. The first silent refresh threw the refresh token away. `store()` wrote `refresh_token ?? null`, and Cognito does not return one on a `grant_type=refresh_token` exchange, so a missing field was read as a revoked credential. The access token lives an hour and the refresh token thirty days: the first refresh succeeded and disarmed every one after it. It now keeps what it holds when the answer carries none. Nothing reacted to `UNAUTHENTICATED`. `clearTokens()` was called from the sign-out button and nowhere else, so the guard kept admitting a stale token and `useLiveSession.load()` rebuilt a session out of claims the API had just refused. A token that cannot be renewed and a `401` are now the same fact, handled once in the HTTP client, which ends the session and returns the browser to the sign-in screen with the reason. An authentication failure is no session; offline and a 500 still degrade. And a query that had failed was rendered as a query still loading, because `isLoading || !data` collapses two states whenever retries are off. `queryState` separates the three, and the dashboard and the template cards use it. RN-SUB-022 is written in `software-vision.md`, with the interface rule in 13.2 and the token lifecycle in `architecture-guide.md` 5.3 and 8.5. Closes #53 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
`whoami` ended the folder-identifier paragraph with `(RN-AGT-020)`, and the vault role-limit validation answered with `(RN-ACC-012)` in the message it returns. Both codes name a line of a document the reader of the MCP surface does not have: there the code resolves to nothing, and an agent either drops it as noise or reads it as something addressable and spends a step on it. Neither sentence loses anything without it. The traceability stays where the other ~50 occurrences already are, in comments and docblocks next to the implementation. A test now walks everything the connector serves — the whoami text with and without a vault to reach, every tool title, description and input schema, every skill, and every entry of the declared notation — and fails on a rule code in any of them, so the boundary is not a habit. RN-AGT-021 is written in `software-vision.md`. Closes #52 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
Every visit to a vault started at the tree, and getting back to the note open in the previous session meant walking the folder structure again. The path of the last note opened is remembered per vault, in `localStorage`, and arriving at the vault navigates to it. Remembering it across devices is deliberately not delivered: it would be a write on every note opened, on the hottest path of the reading surface, against the quota, and carrying an `Authorship` that reading does not have. Two properties keep it from becoming a trap. It fires on ARRIVAL and not on request, so the vault name in the sidebar still reaches the Vault Context instead of bouncing back into the note. And the remembered path is checked against the structure the layout already holds before anything navigates, so a note deleted, renamed or moved is forgotten in silence and the tree is shown — no request, no flash, no not-found line. `noteAt` moves to `trail.ts`, where the route and the resume ask it the same question rather than answering it apart. Every read and write of storage is inside a try/catch, and a browser that refuses site data behaves exactly as before. Closes #72 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
…ing one The notation used to be declared here, in `RECOGNISED_NOTATION`. It is now the MemorySmith Markdown Profile 0.1.0, a specification with a version of its own, and this build implements a version of it and says which. The version is pinned ONCE, in the `catalog:` of `pnpm-workspace.yaml`. Two packages read it from there -- the contracts, which project `profile.json` into `RECOGNISED_NOTATION`, and the frontend, whose reading surface is proved against the same cases -- and a catalog is what keeps them from pinning two versions of one specification. The Discovery conformance test runs the PUBLISHED cases now, and not a transcription of them: it asserts links and facets per case, and it checks that every notation of the two extractors has a case, so the profile cannot grow an entry nothing proves. It also caught the difference between `recognised: false` and "discarded", which the old blanket assertion had collapsed: `title:` is not reserved AND is an ordinary attribute. The reading surface gets a conformance test of its own kind, because a rendering assertion cannot live in a JSON file. It renders each `reading-surface` entry through the real components, and a declared entry with no expectation written for it fails. On its first run it found #71: `TaskItem` reads `node.checked`, which react-markdown 9 does not put on a hast node, so every task box in the product renders as GFM's own disabled checkbox. `get_skill` opens by naming the profile, its version and its address, with the three rings, and the notation table carries the ring of each form. RN-AGT-022 and RN-AGT-023 are written in `software-vision.md`, the three layers and how the profile enters the build in `architecture-guide.md` 11.0, and `knowledge-base.md` 1.1 now says that CommonMark and GFM are the whole list and that every tool renders outside its own spec, GitHub included. Closes #61 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
…s it Clicking a checkbox changed nothing, on any note, in any vault, whatever the effective role. Neither of the two paths the issue listed was it. `TaskItem` decided with `node.checked`. react-markdown 9 hands a component the HAST element, which has no `checked`: the state lives on the child `input` it generates. So the value was `undefined` for every item, the component took its first branch, and what reached the screen was the checkbox GFM renders by default, `disabled`, with no handler on it. The ordinal machinery was never reached and was never at fault -- `node.position.start.offset` is present and correct. Three things had to change together: - the state is read from the child `input`, not from the item; - that input is dropped from the children, because ours replaces it and two boxes in one line is worse than none; - our class is applied AFTER the spread, because `task-list-item` arrives on `className` and was overwriting `task-item`. `WritableContent` now states the text the ordinal is counted over instead of leaning on a default, and refuses to enable the boxes when the rendered text and the original disagree on how many there are. That is not theoretical: `- [[x]]` resolves to `- [x](...)`, a ticked box to any GFM reader, where the bytes have none -- counting on one side and writing on the other would toggle a different item in silence. Found by the reading-surface conformance test added in dd938e5, on its first run, which is the second defect the profile has surfaced this cycle. Closes #71 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
The product had two doors into the sign-in page and they behaved differently, with nothing on screen to explain why. Arriving without a session handed the browser to the provider; signing out stopped at a card with a button that asked for nothing, because the provider owns the credentials. The comment defending that asymmetry named a loop: handing the browser back to a still-warm provider session would sign the person straight in again. It was verified against the deployed pool before this was written, and it does not happen. `signOut()` goes through the Cognito `/logout` endpoint, which ends the hosted-UI session, and the handover that follows lands on a credentials form. There was nothing left for the click to protect. The `signedOut` mark is gone entirely rather than being repurposed: with the sign-out path behaving exactly as a first arrival, the only job it had left was lowering the handover guard, which is what `clearHandover()` already does and what the sign-out path now calls. That keeps the guard armed on the way out, so a handover that comes back empty still stops instead of looping. The `auth.signedOut` string is removed from both locales, since nothing renders it any more. The two branches that keep the button are untouched and now pinned: `withoutSubscription`, which is the one fact a person cannot discover any other way, and `handover`, which is the loop guard itself. The decision is extracted as `decideLogin` so the rule is a test rather than a shape in an effect. Closes #59 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
…ffect `tags`, `created` and `updated` worked by accident of shape and nothing said so anywhere, while the product declared only `maturity` and `reviewed`. The four reserved keys are now named, in en-US, and reserving is DECLARING rather than enforcing: the extractor still classifies them by the shape of the value, so `created: manually` degrades to an ordinary enum and nothing is refused. `title` stays unreserved, because the title of a note is structural and the cardinality ceiling already switches a `title:` key off on its own. Two of the four reach past the counts. A facet of kind `date` matches by PREFIX. The values are canonicalised to `YYYY-MM-DD`, so a prefix is exactly the granularity somebody asked for, and substring let `created:09` mean September and also the year 2009. The kind travels with the note into the content index to make that possible, and it is optional there: an item written before it existed keeps answering by substring instead of the search going silent while a projection is rebuilt. `aliases` join the search index as other spellings of the note, answering wherever the title does -- under `title:` and under a bare term -- and ranking as a title hit rather than as a mention in a paragraph. They do NOT resolve wikilinks: that is #65, it is a different animal, and shipping it by accident here would put an attribute of the content in charge of behaviour without the decision ever being taken. The interface may translate the LABEL of a reserved key and never the bytes, which is the same line PP4 draws everywhere else. RN-DSC-030, RN-DSC-031 and RN-DSC-032 are written in `software-vision.md`, and the shape rule is stated in `architecture-guide.md` 11.3 where a reader finds it rather than only in a code comment. Closes #62 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
`#subject` in the body looked like it organised something and organised nothing. The entry and its conformance cases arrived with the profile in dd938e5, so what was missing was the rule and the half of it the reading surface owns. RN-DSC-033 is written, with the reason rather than only the outcome: two lineages read the inline hashtag incompatibly, as a link and as file metadata, so there is nothing to inherit and any choice diverges from somebody. Here the curation vocabulary lives in the frontmatter, under the Guidance. Reading it would need a third sanctioned reader of content and a real Markdown parser in the backend, against PP4, and in the 1,562 notes of the example vaults one of the three inline matches was a hex colour. The reading surface is now held to the rejection by tests, because that is where a rejection is most easily undone by accident: a chip drawn around `#subject` would promise a grouping that does not exist. The external link is pinned in the same place, for the same reason. Rejecting is not refusing: the bytes are stored and returned as written. Closes #63 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
The granularity of a point was covered and an interval was not, which is the ordinary question of curation: what came in during the first quarter, what has not been revised since March, what was written before a decision. This is the FIRST operator the query language has, so the decision is not only about dates. The comparison is the primitive, because it composes with the boolean operators already there and needs no second syntax for an open interval. The range `a..b` is kept because it covers the common case in one token, and it is defined as SUGAR: it desugars into `>=a` and `<=b` at parse time, so everything past the parser sees one shape and there is one semantics to implement, to test and to explain. Both ends are inclusive, which is what `..` means everywhere a person has met it before. Comparison cuts the value to the length of the operand before comparing, which is what keeps the prefix granularity of RN-DSC-031 usable as an interval end: `created:<=2026-02` includes the fifteenth of February instead of excluding most of the month. That off-by-one would never have been reported, because it looks like an empty result. Two things are refused rather than answered empty. A range with inverted ends is a syntax error at parse time. An interval over an attribute this vault does not hold as a date is refused in `SearchNotes`, once, against the kinds the scan already carries -- because whether an attribute is a date is a fact about the vault and not about the query string. An empty result reads as "there is nothing filed under that", and neither of these means that. RN-DSC-034 is written in `software-vision.md`, and the precedent the shape sets is in `architecture-guide.md` 11.3. Closes #64 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
Every wait in the product rendered one of two things and neither said anything: a blank 60vh of viewport, which is what every page load and every deep link showed first, and one line of grey text standing in for a whole screen. Eight surfaces now draw a placeholder matched to the frame the real content occupies: the application while the session resolves, the vault catalogue, the KPI tiles and the facet charts, the note, a template card, an embedded block mid-paragraph, the folder tree and the graph canvas. One reusable primitive composes all of them, because a generic grey box used everywhere and eight hand-rolled ones are the same failure from opposite directions. `VaultLayout` is the one that pays for itself first. Its frame -- sidebar, brand, search box, navigation, content column -- is known before the request leaves and was being withheld for a structure query, so the whole screen arrived at once, with a jump. It now draws immediately and skeletons only what the query fills. The graph stops swallowing its own failure. It used to catch and render an empty vault, so "nobody has written anything" and "we could not read it" looked identical. A placeholder belongs to `isPending` alone, which is what #53 made possible: `queryState` reads the error first, so a shimmer can never sit over a request that already failed. Every region carries `aria-busy` and keeps the word for whoever hears the page, since a shape announces nothing where the old line at least said "Loading". The sheen stops under `prefers-reduced-motion`, leaving the frame in place. `.loading-screen` is deleted: nothing renders it any more. Closes #58 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
…ences The profile bumped to 0.2.0 and the reading-surface conformance test failed on seven declared entries with no expectation written for them, which is the guard doing its job: the profile cannot grow a notation this interface silently does not implement. `==highlight==`, `%%comment%%` and `^block-id` were already written in the example vaults and did nothing. All three are remark plugins and not passes over the string, because a `==` inside a code fence is not a highlight and only the parser can tell. The comment leaves the PAGE and stays in the bytes, so `read_note` returns it and the export writes it: an agent sees what a person on the page does not, and that asymmetry is declared rather than discovered. `![[note#^id]]` resolves to the identified block, told apart from a section anchor by the marker rather than by trying one and falling back. Math renders through remark-math and KaTeX, with the rule the profile states and the library does not implement. remark-math opens a formula at any `$` and closes it at the next, so `costs $30 a month, and the plan $60` turned the sentence between the prices into mathematics. `remarkMathDollarRule` runs after it and gives back any inline formula whose delimiters break the rule, reading the source through the node position because the delimiters are gone from the node by then. Raw HTML stays unrendered and is now declared, with the payload that matters: a note carrying `<script>`, an `onerror` handler or an `<iframe>` is escaped and shown as text. Superscript and subscript follow from it and have no notation, and GFM's single-tilde strikethrough is switched off for the same reason: left on, `H~2~O` written for a subscript came out struck through, which is a worse answer than nothing. RN-DSC-035 is written for the block reference; the rest are 13.2 in prose, as interface rules are, and the plugin stack is in `architecture-guide.md` 11.0. Closes #66 Closes #67 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
A vault written in an editor that reads `#subject` as metadata arrives here intact and loses the curation those tags carried. That is the declared cost of the rejection in #63, paid at the door by whoever is arriving, and it is worth offering to pay it for them. The issue assumed a capability in the product that reads the inline tags and proposes the frontmatter. That cannot be built here without breaking design rule 5: reading `#subject` for meaning makes the backend a THIRD sanctioned reader of content, which is the exact guarantee the rejection was made to protect. Buying back the cost of a rejection by spending the reason for it is not a trade this product makes. The interface may not do it either -- 13.2 keeps it on the same side of PP4. So the product teaches the method and the agent performs it, which is the shape #21 already established and which the thesis of the product asks for. `get_skill` serves `convert-inline-tags`: - what is NOT a tag, in a table: a heading, a hex colour, `C#`, an issue number, a URL fragment, anything inside code; - propose per note and in full, showing what would be written AND what was rejected, because a rejection you got wrong is invisible unless said; - wait for a person to accept, with "proceeding unless told otherwise" named as the failure mode rather than left implicit; - write one note at a time with `update_note` and its `baseRevision`, so each conversion is an ordinary authored write with a revision of its own, and design rule 10 is satisfied by construction: there is no batch; - leave the body alone, because the inline tag is the author's bytes and leaving it makes the whole thing reversible by deleting one line. Every one of those is a test, because a method that teaches half of them would put an agent halfway through a migration nobody agreed to. RN-PRT-007 is written, with the reason it is a skill and not an endpoint. Closes #68 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
The notation was proved in tests and taught by a skill, and none of that can be read by a person deciding whether to bring their knowledge here. Neither shows it doing its work: a callout that is drawn, an alias that finds a note by its acronym, an embed that expands to a single identified block, a formula, a checklist that writes back. `continuity-engineering` and `enologia` do. Every declared notation of the three rings appears in each of them IN CONTEXT -- the acronyms are the acronyms those subjects live on, the formula is the arithmetic the subject actually uses, the checklist is a runbook -- and so does every rejection, written where somebody would have reached for it and beside the sentence saying what to write instead. That is the half of the profile no other vault will ever show, because a vault written by somebody using the product only contains what worked. They are NOT translations of each other. Continuity objectives and enology carry the same notations through different material, which is what lets the pair show what one vault cannot: the four reserved keys are in en-US in BOTH, while `regiao`, `tipo` and `colhida_em` stay in the language of whoever keeps the vault. The guard runs in both directions, and it is the whole reason a hand-written vault is allowed to be the artefact here: every entry of the declared notation appears in each vault, and neither demonstrates a notation the profile does not declare -- a dataview block, a footnote, a templater expression, an inline field. A declared entry with no detector fails the test rather than being noticed later, which is the same shape the reading-surface conformance uses. They go up and come down with the environment through the existing scripts, with no step of their own. Closes #69 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
`[[Lei 14.133]]` is a real edge: the backend resolved it, the backlink exists, the graph carries the connection. The reading surface drew it as a pending link, telling the person a note they had written did not exist. The slug is computed twice. The kernel produces it for storage and for the link extractor; the interface produces it again to turn a wikilink into a URL, because the frontend takes types from `@memorysmith/contracts` and nothing else from the backend. The second copy implemented four of the six steps of the profile 3.3, missing the `.` or `,` BETWEEN TWO DIGITS -- which belongs to the number and not to the words around it -- and the truncation at eighty characters. So the interface addressed `lei-14-133`, found nothing under it, and rendered a link that resolves as one that does not. It fired wherever a title carries a decimal number, which is where these vaults live: norms, articles, versions, measurements. The two demonstration vaults added in the same cycle are full of them. The two implementations are NOT pinned to each other. Both are pinned to the published conformance cases, which is the only arrangement where the drift is a failing build instead of a screen that lies. Nine of them fail the moment the rule drifts again -- and one, `wikilink/accents-and-digits`, was already in the suite and had never been pointed at this side of the product. That is the fifth defect the profile has surfaced in this cycle. Closes #73 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
Steps 1 to 5 of the version bump flow (development-process.md 9.1): the base version in CLAUDE.md, the thirteen package.json of the monorepo, and the CHANGELOG section renamed with the cut date, with a fresh [Unreleased] above it and the compare links at the bottom. The "Current state" paragraph of CLAUDE.md is rewritten for the cycle. Minor and not patch, and the accumulation decides it rather than any single pull request: `RECOGNISED_NOTATION` and the skill now come from a specification versioned outside this repository, the connector serves a new skill, the query language gained its first operator, the frontmatter gained a reserved vocabulary with two keys that do something, and the reading surface gained the rest of the vault ring. While the base version is 0.x a contract break lands in a minor, which is what the profile import is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
Two checks failed on the pull request, and both passed here for reasons that say more than the failures. `shared/api/source.ts` throws at module load when `VITE_API_ORIGIN` is missing, which is right for the application: the interface has no offline mode, and failing at the door beats failing per screen. But four test files added in this cycle reach that module through a component, so they passed on a machine with a `.env.local` and failed on one without. The test run now declares its own origin in `vite.config.ts`. The value is never used -- nothing under test reaches the network -- and it lives there so no test file has to know and no CI environment has to be told. The lint error is a helper I wrote in the demonstration-vault guard and never used. It was in front of me the whole time: my local check filtered eslint output for " error ", and the `no-console` warning text contains "warn, error", so `head -3` cut the output before the real error line. The filter was matching the noise and hiding the signal. Verified by reproducing CI rather than by trusting the filter: `.env.local` moved aside, `pnpm lint` at zero errors and the whole suite green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
Removing the unused helper left the file outside prettier's shape, and CI runs `pnpm format` as a check. That is the third thing my local verification did not cover, after the lint filter that hid its own signal and the `.env.local` that made the frontend tests pass here and fail there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The cycle in which the product stopped declaring its own Markdown notation and started implementing a published specification.
Nineteen issues, and the shape of the cycle is best read from what the change to a specification cost and returned: five defects surfaced by a suite this repository did not write, two of them in features that had been shipped and were quietly broken.
Summary of changes
The profile: the product implements a specification instead of declaring one
RECOGNISED_NOTATIONis now a projection of a publishedprofile.json, not a list beside it, and the version is pinned once in thecatalog:ofpnpm-workspace.yaml. The Discovery conformance test runs the published cases; a new conformance test in the frontend runs eachreading-surfaceentry through the real renderer, because a rendering assertion cannot live in a JSON file.get_skillopens by naming the profile, its version and the three rings (RN-AGT-022, RN-AGT-023). Closes The product declares its own notation, while the profile it implements now lives outside this repository #61whoamitext, every tool title, description and schema, every skill and every declared notation, and fails on a rule code in any of them (RN-AGT-021). Closes The MCP surface should not cite business rule codes #52The vocabulary, the query and the reading surface
datefacet matches by prefix, socreated:09matches nothing; andaliasesjoin the search index as other spellings of the note, ranking as a title hit (RN-DSC-030, RN-DSC-031, RN-DSC-032). Closes The frontmatter vocabulary works by accident of shape, and nothing declares it #62#tagcarries no meaning here, and the reading surface is held to it by tests, because a chip drawn around#subjectcosts one commit and promises a grouping that does not exist (RN-DSC-033). Closes An inline #tag looks like it organises something, and organises nothing #63==highlight==,%%comment%%,^block-idand![[note#^id]], whose edge is exactly the edge a plain wikilink produces (RN-DSC-035). The asymmetry of the comment — it leaves the page and stays in the bytes, so an agent sees what a person does not — is declared rather than discovered. Closes ==highlight==, %%comment%% and ^block-id are written in the vaults and do nothing #66H~2~Owritten for a subscript came out struck through, which is a worse answer than nothing. Closes Math, sub/sup and raw HTML: the reading surface has no declared answer #67#subjectfor meaning would make the backend a third sanctioned reader of content — buying back the cost of a rejection by spending the guarantee that motivated it. Closes A vault arriving with inline tags loses the curation they carried #68The interface
UNAUTHENTICATED, and an errored query rendered as a query still loading (RN-SUB-022). Closes A session that cannot be refreshed leaves the interface loading forever, and only signing out gets out of it #53aria-busy, and the shimmer stopping underprefers-reduced-motion. Closes A wait shows a blank screen or one line of grey text, and then the whole layout arrives at once #58/logoutthe hosted UI asks for credentials, so there was no loop for the click to protect. Closes Signing out stops at a screen with a button, while arriving without a session goes straight to sign-in #59The defects the profile surfaced
TaskItemreadcheckedoff a node react-markdown 9 does not put it on, so every box reached the screen as GFM's own disabled checkbox. Found by the reading-surface conformance test on its first run. Closes The first write the web interface makes does not work: a task box that toggles on no note #71, Closes Check box toggle doesn't #54wikilink/accents-and-digitswas already in the suite and had never been pointed at the interface: the suite did not need a new case, it needed a second reader. Closes A wikilink to a title with a decimal number renders as pending, because the interface computes a different slug #73Deferred, with the reason written
#48 (an isolated staging environment) and #57 (the product's e-mail leaving from its own domain) moved to v0.6.0: both wait on a process outside this repository, and a cycle whose closing date is set by somebody else's queue is not a cycle. #55 (editing in the browser) stays undecided, because the shape of writing on the web surface is a decision about the product and not about a screen.
AI productivity analysis
2026-09-06to2026-09-06)Estimated human effort (without AI assistance)
The estimate is dominated by three things rather than by volume. Diagnosis: three of the defects had no visible cause — a task box that fails identically on every note, a session that dies two hours later with no message, a link that renders as pending while the graph holds the edge — and each was found by building the instrument that finds it, not by reading the code. Breadth: the diff crosses a published external specification, two remark plugin layers, a query language parser, a projection schema, eight rendering surfaces and two hand-authored knowledge bases. Judgement: four decisions in this cycle were reversals or refusals that had to be argued and recorded — the alias resolution, the inline-tag conversion, the sign-out asymmetry and the
sourceargument of the task ordinal — and each is the kind that costs a day of thinking and twenty minutes of typing.🤖 Generated with Claude Code
https://claude.ai/code/session_01BAMCXmvUPsRoPzj9AQMijZ