feat: 2.0.0 "CyberFraud Edition", four knowledge bases on one canvas - #11
Merged
Conversation
F3 is offered next to ATT&CK in the framework panel, not as a separate mode. It needs no new verb: its bundle carries only `subtechnique-of`, which describes the catalogue and never reaches a canvas. On the canvas a fraud technique is an `attack-pattern` reached through the `uses` and `targets` the matrix already allows. The dataset is distilled from the NATIVE F3 file rather than its STIX bundle, because only the native one carries `isAttack`: 43 of the 123 techniques are ATT&CK techniques reused by number, and the STIX bundle stamps every external reference `mitre-f3`, T-ids included. Reading the bundle we would end up claiming that T1566 is an F3 identifier. Those 43 are emitted as ATT&CK techniques, so a technique reached through either palette is ONE object in the bundle. That is what lets a fraud case and an intrusion case meet. It also forces the naming: our identifiers derive from the MITRE number alone, and F3 spells sub-techniques by full path where ATT&CK spells the leaf, so the build takes ATT&CK's name back and `f3.test.ts` holds that line against future regenerations. The export no longer stamps `mitre-attack` on every technique carrying a number, which was true only while ATT&CK was the sole source and becomes a fabricated reference the moment F1001 goes out. The framework is recorded next to the number, set by whichever palette created it, editable in the inspector, and read back on import. Absent still means ATT&CK, so nothing already on a canvas changed meaning and the golden vectors are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
The tactic chips let you browse F3 by tactic when the search box was empty, on the grounds that a framework you do not know yet cannot be searched. The reasoning holds and the interface still lost: one panel, one switch, and two different things happening depending on the side, discovered by clicking. The dataset keeps its tactics. They are the only thing that makes this framework legible to someone meeting it, and that belongs on a page with room to explain rather than in 180 pixels of palette. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
… two The dropdown offered seven observables where `patternFromObservable` handled thirteen. The second batch (accounts, software, certificates, MAC, mutex, directory) reached the generator through the inspector's "Generate an indicator" button and never reached the builder, so a pattern that was perfectly expressible looked impossible to write by hand. That is worse than a missing feature. Nothing was broken and nothing warned: an analyst concludes the format cannot say the thing and models around it. A sweep in `pattern.test.ts` now holds the dropdown against SCO_TYPES, the way `bridges.test.ts` holds the recipes against the matrix. An account gets two entries rather than one, because `account_login` and `user_id` are not interchangeable: the spec defines the login as what the holder types to sign in. Writing a bank account number in there asserts that someone signs in with their IBAN. The value is passed empty when it belongs in a property, since the resolver reads the observable's value first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
The narrative sorted the story by type: actors, tooling, infrastructure, observables. That is a reasonable guess when nothing better is known, and it is exactly wrong for a fraud, where the case IS the sequence: the eight dated steps of a real BEC came out as a bag. A relation's start_time is the analyst saying when a thing happened, so it outranks the guess. A dated statement now says its day and takes its rank from it, an undated one keeps its place ahead of the sequence, and the rule holds inside a block as well as between them. Clauses group by verb AND day, because one date on two days would be a claim nobody made. A graph with no date is untouched, and a test holds that. Folded in: an F3 external reference now points at the hash route the F3 site actually serves, not the flat path F3's own bundle publishes and nothing answers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
… from A case that crosses from an intrusion to a fraud carries techniques from both matrices, and on the canvas they were indistinguishable: same shape, same colour, same word "Technique". The mark is read off the property and never off the number, which cannot arbitrate in either direction: F3 reuses 43 ATT&CK numbers, and publishes T-numbers of its own that an import can legitimately bring in as F3. Only F3 is marked. Absent means ATT&CK everywhere else in the app, most graphs are made of ATT&CK, and a chip on every technique would say nothing on either. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Three things the fraud case surfaced, all in the same corner of the app. An email address reached the indicator bridge and nothing else, with none of the reasons mutex and directory have for staying out: an address you send phishing from is something you acquired, like the domain that hosts the page it links to. It joins the infrastructure parts. The part recipe now words itself after the observable. STIX has no SDO that carries a bank account, `infrastructure` is the closest and it is the object that makes one detectable on a platform that only acts on objects, but "infrastructure this observable is part of" offered over an IBAN reads as a category error and the analyst walks away from the one route that works. Same object, same bundle, different question. And the infrastructure types are offered at last, from the vendored OASIS schema, with a test holding the list in step. The schema declares plain strings and polices nothing, so the form is where the line holds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
…h one it is Both frameworks are named on a card now, not just the newcomer: marking the exception works while there are two and reads as an oversight the day there is a third. The export, the form and the card read one registry, so adding a framework is one entry. Ctrl+K gains an F3 group, F3's own techniques only: the 43 it borrows are already under ATT&CK and build the same object. And an account name says which of the three names it is. account_login is what you type to sign in, user_id is what the system calls the account, and an IBAN written into a login was a fabricated claim whose identifier belongs to an account that does not exist. The default stays account_login so nothing already drawn moves, the form hides the field the name occupies, the generated pattern follows the same choice, and the import records what it read - an account known only by its user_id used to come home as a login, which is another account. A display name identifies nobody. STIX answers that with a random identifier, which is the one thing this application cannot do, so the export refuses and the lint says so first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
The palette finds F1005.003 and says nothing about what F3 is. That is fine for ATT&CK, which is common knowledge in this trade, and no use at all for a framework that is a year old: a framework you do not know yet cannot be searched, and the reading does not fit in 180 pixels of palette. So two pages of their own, /attack and /f3, and not a section of the STIX guide: that page teaches a FORMAT, these teach a BODY OF KNOWLEDGE, and the reader of one is rarely the reader of the other at the same moment. What the framework is and is not, how to read a number, the trap that F3 also publishes ATT&CK numbers, and what a technique becomes once it is here. Derived where it can be: the relationships come from the matrix, like the rest of the guide. Written down where it must be, since the frameworks live outside this repository, and held in step by a test - the eight F3 tactics on the page are the eight in the shipped dataset, or the build says so. Same shape as /about: prose, no script, prerendered at build time, and the same component served inside the app at #/attack and #/f3. VerbList moves out of the guide to be shared rather than copied. The way in is two named links under the search box, under a rule, both of them whichever framework is selected: an icon beside the title had to be found and then guessed at, and "what is the other one" is exactly the question somebody in front of that switch is asking. A new tab, because the reader has a canvas open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
The F3 build read a pinned `f3-v1.1.json`. The native series is published one file per version with no unversioned copy, so a v1.2 release would have shipped and the Monday job would have kept rebuilding v1.1 and reporting "unchanged" about a framework that had moved. It now lists what MITRE published and takes the highest version, comparing numbers as tuples so that f3-v1.json does not outrank f3-v1.1.json, and names the file it read. Deliberately not `src/data/matrix-data.json`, which is what their site builds from and is always current: it is working state on a branch, and we ship what MITRE published. The weekly job also gained a reason to keep going when the tests fail. What fails there is a disagreement between the regenerated files and something written down beside them - a name arbitration, or now a number in the framework pages, which `frameworks.static.test.tsx` holds against the datasets. The fix belongs on that branch, next to the diff that caused it, so the pull request is opened either way, says which of the two happened, and the run then goes red so the notification still fires. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Written straight after F3 went in, while the order of the work and the reasons behind each choice were still exact. Four questions to answer from the framework's published data before writing anything, the files to touch in order, and the traps that have already been paid for: a STIX bundle that loses the provenance its native file carries, a pinned version file that makes the weekly refresh blind, two spellings of one number that put two cards on the canvas, and an identifier shape that decides nothing. CONTRIBUTING points at it, and its dataset section now counts four corpora rather than three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
…othing MITRE's Adversarial Threat Landscape for AI Systems: 178 techniques, 16 tactics, beside ATT&CK and F3 in the same panel and on the same canvas. An intrusion that ends in a poisoned model is one graph. Where F3 reuses 43 ATT&CK numbers, ATLAS reuses none: everything is an AML number of its own, and the 37 techniques adapted from an ATT&CK one record which, in a field the page reads and no bundle ever claims. MITRE's own ATLAS bundle does not claim it either, which settles it. The 36 techniques whose name also exists in ATT&CK are two cards with two numbers, which is what they are, and the mark on a card is what says so. The framework itself cost a build script, a registry entry, a loader and a page. Nothing else moved, and that was the point of the previous commits: both palettes, the export, the import and the extraction read the registry, so the switch, the search, the Ctrl+K group, the reference written and the one read back all came for free. Two hardcoded `mitre-f3` comparisons were the only holdouts, and they were bugs waiting for exactly this. The build follows MITRE's own pointer chain to the current release rather than pinning a file, ATLAS shipping one every month or two. Left out and said so on the page: 37 mitigations, which are course-of-action objects this canvas has no card for, and 68 case studies, which are not STIX. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
The palette shipped Enterprise and nothing else, so a SIM swap or an attack on a PLC had to be typed into the MITRE ID field by hand. The three are merged into one corpus rather than put behind a selector, because they are one knowledge base: one identifier space, one `mitre-attack` reference, and not one number in common. That last part is the only thing that could have made this hard, so the build checks it at every run instead of trusting today's answer: the same number under two names would put two cards on the canvas for one identifier. 918 techniques instead of 697, plus 126 malware families and 6 groups. A result that is not Enterprise carries its matrix, in the panel and in Ctrl+K: T0800 answers a question nobody working an office intrusion asked. Two consequences worth naming. Each domain names its own kill chain, so reading only `mitre-attack` would have stripped every Mobile and ICS technique of its tactics. And the corpus growing moved five actors out of the MISP galaxy, which ATT&CK now resolves itself, while three of the five identifiers F3 flags as ATT&CK and could not resolve turn out to be Mobile: they are checked against ATT&CK now rather than trusted, and the spellings agreed all along, which is precisely what nobody could tell before. The datasets move to v19.2 in the same pass. Cloud, Containers, ESXi and Network Devices are NOT domains: they are platforms of Enterprise, and their matrices on the website are filtered views of a corpus we already ship whole. Said on the page, so nobody goes looking for a dataset that does not exist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
MITRE's Adversarial Actions in Digital Asset Payment Technologies: 68 techniques over eleven tactics, about exchanges, wallets, bridges and the consensus underneath them. It belongs here for one reason above the others: F3 ends at Monetization with "Convert to Cryptocurrency", and AADAPT's own eleventh tactic is called Fraud. A case can now run from a phishing message to a wire transfer to a bridge without leaving the canvas. It is also the hybrid of the two arrangements already shipped. Ten of its eleven tactics ARE ATT&CK tactics by identifier, the way six of F3's eight are, which costs nothing since nothing here creates a tactic object. Every technique is its own, the way ATLAS's are, and the four adapted from an ATT&CK technique are numbered after it (ADT1195 from T1195) without ever claiming to be it: the export writes mitre-aadapt and nothing else. Two traps in their data, both paid for once. Their `ATT&CK-reference` repeats the AADAPT identifier in its id field, so the ATT&CK number is only readable from the url. And their file says `version: 4.4.0`, which is the ATLAS data version their tooling was forked from, along with its CHANGELOG and its LICENSE: the repository has no tags and no releases. This dataset therefore ships no version at all, the panel shows none, and a test forbids 4.4.0 from appearing anywhere in it. Licence: Apache 2.0 on the data, public release unlimited, case 25-1204. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Four catalogues now share one identifier space in this application and an identity derived from a number alone, so what matters is no longer what each does but what they do to each other. The audit is kept as a test: no number claimed by two frameworks under two names, every number the palettes produce accepted by the form, every technique exporting the reference its framework claims and nothing else, all of it validating against the OASIS schemas, and export-import-export stable. It held. Two things it found, both fixed here. The form did not check a number against the framework beside it. The shape cannot say which framework a number belongs to, since F3 publishes ATT&CK numbers, but the reverse direction is decidable: ATT&CK publishes no `AML.` number, so an ATLAS number left under ATT&CK was about to leave as a fabricated ATT&CK reference. The shapes moved into the registry, the warning names the framework that does publish it, and it never blocks. And the canonical bridges offered a detection indicator for an account named by its display name, which identifies nobody: no pattern could be built, so the recipe promised in one click an object the export refuses. A second problem where the analyst had one. The bridge is now offered only when there is a pattern to put in it. Also corrected: docs/identifiers.md said the ATT&CK id wins for an attack-pattern, which has been four frameworks' numbers for a while. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
…e first The scenarios were all intrusions. Three fraud ones now: account takeover, card data theft and reuse, and crypto cash-out, written on F3 and AADAPT numbers with the framework named beside each. They live under a heading of their own, with FOVI moved in beside them where it always belonged. And the answer to the question that came with them, which was no. Two templates naming the same actor drew two cards, and that is worse than untidy: both collapse onto one STIX identity at export, so the second card's description was quietly the one that lost, with a warning nobody reads until the end. A scenario now attaches to the objects already drawn, which is the rule the document import and the enrichment already follow, and the labels of the second scenario are added to what it reuses so an actor in two cases carries both. A relationship both scenarios assert is drawn once. The decision is a pure function tested on the real templates rather than logic buried in a click handler: what to create, what to reuse, which labels to add, which links are new. Verified in the browser on the two that share an actor and a victim: nine objects and eleven relationships instead of eleven and twelve, one connected graph, and no duplicate card. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Three was a floor rather than the answer. The fraud family now covers the three ways money actually leaves: a transfer the victim makes themselves (the fake adviser call, which compromises nothing at all), one made in their name (account takeover, SIM swap, card reuse), and one that never touches an account (the mule network, crypto cash-out, the token scam). Each is a different shape rather than a re-skin: the vishing one has no compromise in it, the SIM swap enters through the operator, the mule network is the cash-out layer alone with one account per line, and the token scam happens on the chain rather than beside it. With them, a test that holds every number a scenario carries against the shipped corpora, name included: a `fixed` technique is a number typed by hand into a JSON file, the identifier derives from it, and nothing downstream would catch a typo. It found one on its first run, in a scenario that predates all of this: FOVI pointed at T1656, which ATT&CK revoked in favour of T1684.001. Corrected, and it would have gone out as a reference to a technique MITRE no longer publishes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
…ee families ATLAS shipped this afternoon with nothing using it. Three scenarios now: prompt injection against an agent that leaks through its own tools, a poisoned model taken off a hub, and a model extracted through the API it is served from. Each is a different shape rather than a re-skin, and the second one is the only scenario in the set whose payload is a file with a hash. A fourth crosses on purpose and sits with the frauds, where the case belongs: a deepfake-assisted transfer is ATLAS for the face and the voice, F3 for the money. The family says what kind of case it is, never which framework it draws on. And the panel had to change shape for it. Twenty-six scenarios under three headings meant scrolling past two families to reach the third; it now switches between them behind the same chips the framework panel uses two icons above, each carrying its count. One line per family rather than three columns: "AI systems" does not fit in a third of a 180 pixel panel, and three truncated words are worse than three lines. A test refuses a family holding a single scenario, since a heading over one thing is a heading over nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Ordering the dated statements in place was half an answer. It kept each subject's timeline inside that subject's block, so a case whose actor and whose malware are both dated had two timelines to interleave by eye. And it let the undated bulk a scenario generates - nine "the technique targets the victim" - open the story ahead of what actually happened, which is what modelling a full case in the browser made obvious. So two named parts. A Chronology carrying every dated statement from every subject on one timeline, each line saying who did what that day, and then Undated, in the reading order of the attack chain it always had. Naming them is the point: an order the reader has to infer is an order they can misread. A report carries one thing the panel does not: the same chronology again, one timeline per subject, and only when several subjects are dated. One actor doing everything would print the same list twice under two headings, and a 300 pixel panel read while working wants the case rather than the case and its index. All four renderers - panel, Markdown, report image, PDF - go through the same two helpers, so the sentence and its capitalisation are decided once. A graph nobody dated has no chronology, hence no headings: it reads exactly as it did before, and the test that says so is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Exporting a real case showed it: three of the six per-subject timelines held a single line each, which is not a sequence and reads as padding between the two that are. A subject now needs more than one dated event to get a timeline of its own, and the section still needs two such subjects to appear at all. Its events are in the global chronology either way, which is where a lone dated fact belongs. On the case it was found with, the section went from six subjects to the two that have a story: the actor, and the cash-out chain that gained an IBAN on the 19th and a wallet on the 24th. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Dropping it made the by-subject section lie: a reader looking up what the domain did, finding no entry, concludes it did nothing dated. Every dated subject is listed again, and the one with a single event reads as a line rather than as a heading over a list of one, which is the rule the story blocks have followed since they were written. The section still needs two subjects to appear at all, for the reason it always did: one actor doing everything would print the same list twice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
STIX has no day. `start_time` is an RFC 3339 timestamp with seconds and a `Z` required, so our day-only window has always gone out as midnight UTC, and an imported hour was worse off than that: truncated to its day on screen, and overwritten the moment somebody edited the date. A relationship now carries an optional time beside its date. Two fields rather than one `datetime-local`, because leaving the hour empty has to stay a way of saying "that day" instead of a way of asserting midnight, which is the same distinction this application keeps everywhere else. The chronology orders, groups and reads to the minute: two transfers on one day are one line only when nothing distinguishes them, and an hour distinguishes them. Midnight exactly is read back as a day. Everybody, this application included, writes T00:00:00Z for "that day, hour unknown", so reading it as an hour would turn every imported day into a claim nobody made and would make our own export-import cycle reword the narrative. And the chronology can be drawn: an option at export puts a mermaid timeline above the list in the Markdown, where a diagram is generated rather than composed. It is a shortening and behaves like one, dropping the scheme of a URL, whose colons are the diagram's own syntax, and cutting a label past seventy characters. The list under it is the same chronology in full: that one is to read, the drawing is to look at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
React Flow calls onSelectionChange on every internal update and not only when the selection moved. We answered with a fresh array of ids each time, React re-rendered, React Flow called back, and the canvas span at several hundred "Maximum update depth exceeded" a second from the moment an investigation opened. Nothing looked broken, which is why it lasted: the canvas was merely slow, and anything asking the main thread for real work behind it, a report export in particular, waited its turn. The ids are compared before the state is replaced, so an unchanged selection now changes nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
The mermaid timeline landed in the Markdown only, on the argument that Markdown is where a diagram can be generated rather than drawn. That argument was about how to make one, not about who wants one, and the report image and the PDF are exactly the formats handed to somebody who will not open a Markdown file. So the same rows, painted: a rail down the margin, a dot per moment, its events under it. timelineRows() is now the one grouping the three renderers share, so a case reads the same whichever file it leaves in. The PDF draws its rail line by line rather than block by block, so a page break cuts it where the text is cut instead of leaving it running into the margin. The checkbox is no longer tied to the Markdown, and stays optional and global: a drawing per subject would be several pictures saying what the by-subject lists already say in less room. The image report and the PDF also name their parts now, the way the Markdown does. Both ran the global chronology, the per-subject one and the undated blocks together with nothing between them, which was readable while the narrative had one section and stopped being so when it grew three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
The narrative grew from one section to three and the report image and the PDF still had two typographic ranks: a small uppercase title, and bold at text size for everything under it. So "Chronology, by subject" and "The threat actor Guilde Vermeil" arrived as the same kind of thing, one naming a part of the report and the other a subject inside it. Three ranks now, each saying what it is by a different means rather than by being a point bigger than the one below: a section is small uppercase in the accent, tracked out and ruled off; a part of it is bold in the text colour; a subject inside that part is bold and quiet. And more room between them, which is most of what makes ranks legible at all. The by-subject section also had a lone dated line reading as the end of the block above it, with its date sitting in the middle of a sentence: "The infrastructure Kit AiTM Vermeil 2026-08-10 Consists of the URL...". The one-off subjects are gathered ahead of the blocks now, and each of those lines opens with its moment like every other line of the section, naming its subject in the sentence rather than before the date. In all three formats, since the ordering comes from timelines(). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Three things, all of them about the page. The graph had a page and used two thirds of it: a graph is never the shape of an A4, and the third left over was white, so a dark picture arrived stamped on a white sheet. The page is painted in the canvas background before anything else is drawn on it, so the leftover is the colour of the picture and the eye reads one page. The graph is still scaled to fit whole rather than cropped to fill, because a cropped graph is a wrong graph. The prose ran the width of a landscape A4, about a hundred and thirty characters to the line. The graph page keeps the orientation that suits the graph; every page of prose after it is portrait whichever way that one went, which needed the page breaks to name their format rather than inherit the document's. And a section starts its own page. A title landing three lines above the fold is most of what makes a document look thrown together. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
Four knowledge bases on one canvas, a case that can run from a phishing message to a wire transfer to a bridge, a narrative that reads it in the order it happened, and a report laid out to be handed to somebody. The answer to "what is this for" changed, which is what the major number is for here. Nothing breaks: bundles stay valid, canvases written by 1.x open exactly as they did, the file format has not moved. Said in the release notes too, because a 2.0 makes people ask. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH
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.
Four knowledge bases on one canvas, and a case that can cross between them.
What is in it
frameworks.tsand a build script, not a change spread across the app. The recipe is written down indocs/adding-a-framework.md.Compatibility
Nothing breaks. Bundles stay valid, canvases written by 1.x open exactly as they did, the file format has not moved. The major number is about what the tool is for.
Checks
698 vitest,
tsc --noEmit, eslint and the production build all clean. Modelled end to end in the browser on a 24 object / 32 relation case crossing the four frameworks: bundle validates, no internal key leaks, roundtrip exact.🤖 Generated with Claude Code
https://claude.ai/code/session_01EZKZRDMVAdrWUQP2crYVXH