In practice
The adoption log: Streamlane and Driftline
Coral Reef Ventures is adopting Intentset in two of its own products: Streamlane, a work management product, and Driftline, which watches how a product is used. Neither is released. This log records what Intentset did for them, what it cost and what it changed in Intentset, newest first.
Each entry says what happened, what it achieved, what it cost, and what changed in Intentset as a result. A word with a dotted underline has a short explanation: hover over it, or tap it. For anything else, use Ask the docs, the button at the bottom right of the page. It answers from this site's documentation and links to its sources.
Both products' repositories are private, so this page counts and describes their records rather than linking to them. A number like Streamlane #352 is a pull request in that product's repository.
Where it stands
Counted on 2026-10-19 on each product's main branch, with Intentset's own checks (the 0.8 release) and from the records themselves.
Streamlane
Streamlane's check reports 0 errors and 42 warnings, in strict mode with no baseline. Errors fail a change; warnings are only reported. The warnings are:
- 24 on slices with files the check cannot place in one layer, so their imports are not layer-checked.
- 17 imports, from the app's route pages, of a slice's private file instead of its entrypoint. The pages lie outside the declared scope, so they are reported and do not fail.
- 1 slice that uses a backend whose registry entry does not list it.
Of the 684 verification records, 352 name a browser test. Their evidence is checked at each release rather than on every commit, because one change to a slice many others depend on makes most of their records stale at once. The 94 published help articles are all approved, and 79 carry tips for the controls they explain.
The baseline went from 472 when the check was switched on to 0 on 2026-10-11, while Streamlane grew from 37 slices to 43. Nothing has been tolerated since.
Driftline
Driftline's check reports 5 warnings, all on drafts: four slices whose code is not written yet and that implement no behavior, and one rule no behavior names yet.
2026-10-16 to 2026-10-18: Usage evidence reaches the help
Streamlane's backend already named, in one line, the behavior each call delivers, so Driftline could count what each behavior saw. The help assistant, which answers a person's question from the reviewed help, was the one part that named nothing. From 2026-10-16 it does (Streamlane #1001): an answer, a refusal, an answer marked unhelpful and an admin's usage report each name their behavior, and a question that hit the daily cap names none. The line carries the behavior, the outcome and the organization, never the question.
By 2026-10-19, 117 of Streamlane's 318 behaviors were named in its backend code. On 2026-10-18 Driftline moved to the second version of its usage evidence format (Driftline #56) and wrote down how it reads an outcome's evidence, with Streamlane's first readings (#57). Streamlane's product owner set the first target on one of its outcomes, a median time blocked under three days, before any baseline exists; the measure record says so.
What it achieved
- The help can be judged by use. How often a question is answered, how often the help has nothing to say and which articles are marked unhelpful are now counted by behavior, from the same line every other call writes.
- Nothing private was added to the log. The rule set when usage evidence started, no person and no content, held for the one feature whose input is a person's own words.
What it cost
- A behavior named in code is a promise. Each name says the call does what the record describes, so the names had to follow the outcome, not the operation: a capped question is not an answer.
2026-10-14 to 2026-10-15: Evidence at L3, measured on a real product
Run evidenceA record that one check passed, failed, was skipped or errored, at a particular commit, in a particular environment. is what moves a claim from "a check is linked" to "the check passes now". Streamlane began collecting it in two ways. On every push to main, a workflow reruns the unit and contract tests and imports their reports as run records. The browser tests need a deployed backend and take about half an hour, so they run on a shared sandbox and their records go to a store outside the repository.
0.7.0 made a record currentA pass still counts while what its check is about is unchanged: the behavior, the slices that deliver it, the test and what it imports. for as long as what its check is about is unchanged, rather than for one commit. The first full browser run, judged at a later commit, was current for 5 of 274 verifications. Two causes, both measured:
- A backend change reached every page that talks to it. 133 of the stale records went stale because five backend files changed. 0.7.1 counts a backend's files only where a test exercises that backend, and warns about the rest.
- A central slice reached almost everything. One slice that nearly every page depends on moved 227 of 237 subjects. Counting such a dependency only through its declared surface was tried against the history first. It kept 5 current on Streamlane, no better, because that surface had really changed, so it shipped as an option and the default stayed.
The same import also showed a bug: a test that verified two behaviors gave a record to the first and silently none to the second. 0.7.1 gives each its record.
Streamlane then settled where each kind of evidence is checked. The unit and contract tests are checked on every commit: on 2026-10-19, 331 of 332 had a current pass. The browser tests are checked at each release, by the exact commit. A production deploy is refused for a commit no full, passing browser run covers, unless a person gives a reason that is written to the run's log (Streamlane #990). A full run on 2026-10-15 ran 655 tests: 643 passed, 10 were skipped, and 2 failed under the load of the full suite and passed when run alone.
What it achieved
- The model says which claims hold today. A behavior's status in the export is a current pass, a stale one, a failure or nothing, from the product's own runs.
- The rule was tested on a product before it was kept. Each change to what counts was measured on Streamlane's records and on Intentset's own history, and the version that did not help did not become the default.
- A release ships with its evidence. The gate asks for one thing, a full passing run of the commit being shipped, and the person who overrides it says why.
What it cost
- Browser evidence is stale between releases, by design. With a central slice that changes often, most browser records stopped counting within hours of the run. Checking them per commit would mean a half-hour run per commit.
- A full run finds load as well as bugs. The two tests that failed only in the full run took a rerun to tell apart from real failures.
- Records are history. A record made under the older rule reads stale until it is imported again, so the store was rebuilt from a fresh run.
What changed in Intentset
0.7.1 (2026-10-15): a backend's files count only where a test exercises it, a dependency can count through its declared surface as an option, two new warnings say what moved without making a record stale, and a test that verifies several behaviors gives each a record. Intentset's decision on evidence binding was amended twice, each amendment with the measurement behind it.
2026-10-14 to 2026-10-15: Specifications 0.2 and export 0.4
0.7.0 moved the specifications to 0.2 and the export to 0.4, which carries how each record's evidence is judged. Streamlane moved in one pull request (#937): one command rewrote the version lines of 750 records and left every other byte alone. Driftline moved the next day (Driftline #33), and since 2026-10-13 it fetches Streamlane's latest export from Streamlane's main branch rather than reading a checkout (#31).
What it achieved
- A breaking change was one command. A reader of 0.2 refuses a record that says 0.1 and says how to move it, so no product could half-move.
- Driftline reads what Streamlane ships. It reads the export Streamlane's own checks produce, not a copy of the repository.
What it cost
- Every reviewed article had to be reviewed again, mechanically. A reviewed help article is pinned to the exact bytes of the records it explains. The migration changed every record's bytes, so 270 pins were renewed in the same change.
- The help went dark on main until the same day's fix. The published help moved to the new version, and the app's help reader, still on the old one, refused it and showed "No help is published" until it was upgraded (Streamlane #950).
- Every open change conflicted. A change to every record collides with every pull request that touches one, so the move was merged quickly and the others rebased with the same command.
What changed in Intentset
intentset migrate is the one command that rewrites records, and it refuses a tree with uncommitted changes so the rewrite is a change of its own.
2026-10-11: The help is published from the model
On 2026-10-11 Streamlane approved and published 48 help articles from its model, with their tips on the controls they explain (Streamlane #635). Each article is pinned to the records it explains: when one of them changes, the article is no longer reviewed and drops out of what is published until a person reviews it again. A check on every pull request holds the published files to what the model says, and reports the behaviors that have no tip yet. By 2026-10-19 there were 94 articles, 79 of them with tips, and the help assistant answers only from them.
What it achieved
- Help cannot outlive the behavior. A change to a behavior takes its explanation out of the product until someone reviews it.
- The gaps are a list. Behaviors with no tip are reported, not discovered by a reader.
What it cost
- Generated files in every change. The published help names the model it came from, so any change to the model changes it, and two changes that both touch the model conflict there. The fix each time is to publish again after rebasing.
- A change to a behavior is also a review. Editing a behavior an approved article explains takes the article out of the published help, so a small wording fix has a second step.
2026-10-10 to 2026-10-11: The baseline reaches zero
By 2026-10-10 the baseline held 469 violations. Streamlane wrote a plan to retire it (#533) and followed it in steps, each its own pull request with the gate on. The imports that reached past a slice's entrypoint became imports through it, so every slice's entrypoint is a plain list of what other slices may use. Pieces several slices shared moved out into shared code, and the app's shell became compositionCode above the slices that wires them together. It may use each slice only through its entrypoint, and no slice may import it.. The plan's first step took the baseline to one entry, the dependency cycle.
That cycle was the hard part. 25 of the product's 39 slices formed one loop, joined by 99 imports, each reaching every other. It took four more changes (#575, #583, #587 and #596), after a plan for it (#563). The list pages opened the item page, and the item page opened the lists, so the item page became a slice of its own and the lists now reach it through a slot the app's root fills. The same was done for the docs editor and the live views embedded in a doc: the editor is given them instead of importing them. Each change replaced the one baseline entry with a smaller cycle: 25 slices became 22, then two cycles of 13 and 2, then one of 12, then none.
98 artifacts, 0 errors, 92 warnings
Architecture (L2, strict mode): 43 active slices; 258 files claimed,
0 unclaimed, 783 out of scope (counted, not reported); 1196 import edges,
333 across slices; 0 cycles; 0 excepted, 0 baselined, 0 stale baseline entries
The baseline file is deleted, and the model gate runs in strict mode.
What it achieved
- No known violation is left to tolerate. A change that adds any structural violation, including a new dependency loop, fails before it merges. The rule "the baseline only shrinks" is replaced by there being no baseline.
- Slices can be changed one at a time. With the loop gone, a slice's records can be promoted past draft without a cycle in its dependency graph.
- The ending was measurable at every step. The loop's size, 25 slices, 22, 13, 12, 0, was reported by the check after each change.
What it cost
- A cycle's baseline entry is rewritten each time it shrinks. Each smaller cycle needed a new entry, and a rule of its own ("only replaced by a smaller cycle") for the baseline to still only shrink.
- The check cannot see load order. The last change passed every Intentset check and then failed 12 of the product's browser tests: the registration that gives the editor its blocks ran from a file the server never loaded on the client. Intentset reports who imports whom, not when a module runs. The fix rendered the registration from the app's root.
- Seams that cost a faster test run. Because the root now wires in the item page and the editor's blocks, a change to almost any web code reaches almost everything, so the product runs its full browser suite for most changes.
- Records stayed drafts. Retiring the cycle added slice records and changed others; each is still a draft until someone approves it.
What changed in Intentset
Nothing yet. Whether a cycle's baseline entry should survive the cycle shrinking is a candidate for the next release.
2026-10-07 to 2026-10-10: Each area becomes a backend
Streamlane is splitting its backend into areasAn area is a part of the product with a backend of its own: its own data, functions and deploys., and on 2026-10-07 it declared them to Intentset (Streamlane #374). That switched on the AMP rulesThe checks in Intentset's profile for TypeScript apps built on AWS Amplify. They cover how separate backends fit together., which check how separate backends fit together. In that pull request's words, until then "AMP007 to AMP011 were evaluated against nothing." The first run came back clean on four of them: no backend imports another's code, no name is declared by two backends, no code reaches into another area, and the app talks to all of them through one client. Two more rules stay manual review, because they depend on deployment settings the checker cannot read from source.
Declaring the areas also flagged 30 slices that belonged to areas with no backend yet: delivery, docs and work. Streamlane kept those slices in their real areas and added the 30 findings to the baselineThe list of violations that existed when a rule started applying. They are tolerated; any new one fails., rather than pretend they belonged elsewhere. The baseline then shrank as each area got its own backend:
| Date | Change | Baseline |
|---|---|---|
| 2026-10-06 | The check is switched on (Streamlane #352) | 472 |
| 2026-10-07 | Areas declared; 30 slices in areas with no backend yet (#374) | 502 |
| 2026-10-08 | Delivery gets its own backend (#405) | 498 |
| 2026-10-09 | Docs gets its own backend (#415) | 492 |
| 2026-10-10 | Work gets its own backend; none of the 30 left (#489) | 472 |
| 2026-10-10 | A second shared backend package declared; one old entry gone (#503) | 471 |
| 2026-10-10 | A feature change fixes one import on the way (#505) | 469 |
Each new backend is declared with a resource recordA record for something slices use at runtime, such as a backend. It lists the slices that use it, and each of those slices lists it back., such as RES-WORK-BACKEND, that the slices using it list. The check on Streamlane's main branch now reports:
89 artifacts, 0 errors, 603 warnings
Architecture (L2, migration mode): 39 active slices; 233 files claimed,
0 unclaimed, 738 out of scope (counted, not reported); 1176 import edges,
495 across slices; 1 cycle; 0 excepted, 469 baselined, 0 stale baseline entries
What it achieved
- The boundaries between backends are enforced, not hoped for. Any change that makes one backend import another, declares a name twice, or reaches across an area now fails before it merges.
- The split had a measurable finish line. Each area's findings disappeared from the baseline when its backend was declared, so "is this area done?" had a number for an answer. When work's backend landed, none of the 30 was left.
- The baseline ended lower than it started, 469 against 472, though declaring the areas first added 30.
What it cost
- A link made one way only. The delivery backend's record named its four slices, but none of the four slices was changed to name it back. The check reports each as a warning, and all four are still open. The docs and work changes updated both sides.
- Findings to sort. Declaring the areas raised 72 findings, not 30. The other 42 came from two pieces of code that read as owned by nobody: the search backend and the package every backend shares. Declaring a resource record for one and a shared region for the other settled them, so they never entered the baseline.
2026-10-07 to 2026-10-10: The drift gate in daily use
The drift gateA check on every change. If a slice's code changed but its records did not, the change fails, unless a commit says that no behavior changed. asks one question of every change: if a slice's code changed, did its records change too, or does the change say why not? From 2026-10-07 to 2026-10-10 the check ran 367 times on Streamlane's pull requests, and 355 runs passed. Nine of the 12 failures were the drift gate's: a commit changed a slice's code and said nothing about it. They came from 8 pull requests, one of which changed seven slices at once:
Code changed, records unchanged (7, 7 not acknowledged)
Each passed once a commit added one trailerA Key: value line at the end of a commit message. Intentset reads Intentset-Unchanged: followed by a slice's ID as a statement that no behavior of that slice changed. per slice; that one carried seven. Two failures were the help check described in the next entry, and the rest failed before the check ran.
What it achieved
- No code change merges unexplained. Every merged change to a slice's code since the gate went on either updated its records or said, slice by slice, that behavior did not change. Nine times in four days, the gate stopped a change until someone said so.
- The cost of the question is small. 355 of 367 runs passed without anyone noticing the gate was there.
What it cost
Driftline's gate failed twice on one pull request (Driftline #25) with the trailer present. The fix commit says why: "The previous empty commit put Intentset-Unchanged in its own paragraph above the Co-Authored-By line, so git's trailer parser, which reads only the last block, never saw it." It passed once Intentset-Unchanged: SLICE-BACKEND-SIGNALS moved into the last paragraph.
What it changed in Intentset
The agent guide now says the trailer goes in the commit message's last paragraph, and init --agents and guide write that sentence (Intentset #39, 2026-10-10, not yet released).
2026-10-08 to 2026-10-10: The second capability's help, checked in CI
Streamlane's second capabilitySomething the product lets people do, made of several behaviors. got its decision approved and its help published. Its decision recordA short document recording a decision and the reasons for it. An ADR., ADR-0061, was approved and rewritten to cover only the first release (Streamlane #407). Four of its behaviors moved to later releases, and three new release labels keep them out of the help, so the help never describes something that does not exist yet. The next day four of its behaviors were named in code, from BEH-DEPMAP-START to BEH-DEPMAP-LIMITS (Streamlane #418). The product owner approved its knowledge recordA record written for people rather than for checks: an explanation that can be published as help in the product., KB-DEPENDENCY-MAP, after its text was corrected to describe only what the release has (Streamlane #440).
What it achieved
- Help cannot vanish without anyone noticing. A change to a behavior had silently taken blocked work's help off the product (below). Streamlane now publishes the help on every pull request and fails the change if the result differs from what is committed. Tested by appending one line to a behavior, the check failed as it should.
- Help describes only what is released. The release labels keep the four later behaviors out of it.
- Four more behaviors can be counted in use, now that their code names them, and two more records are approved, ADR-0061 and KB-DEPENDENCY-MAP.
How the gap showed
Streamlane #418 edited BEH-BLOCKED-SHOWN, one of the behaviors that KB-BLOCKED-WORK is pinnedA knowledge record lists the behaviors and rules it explains. If any of them changes, the record goes back for review before it is published again. to. That sent the knowledge record back for review, as pinning is meant to. But nobody noticed until the next publish, which wrote 0 documents and deleted the committed help for blocked work. The record was reviewed again, and the check above was added. Three later changes each re-pinned KB-DEPENDENCY-MAP so it would not drop out silently.
What it cost
- A check that could not pass. The first version compared the publish's output with a plain diff. Every publish records a new commit and fingerprint, so the check failed on its own pull request, twice, until it ignored those two fields.
- A refusal with no reason given. The publisher refused a knowledge record whose text repeated its title, giving only the reason
title, "and no further explanation; I bisected it paragraph by paragraph." - An exact heading. The validator requires a section named exactly
Decision, so ADR-0061 says which release it decides in that section's first line, not in its heading.
None of the three has changed in Intentset yet.
2026-10-08: Driftline decides what a drafted model is worth
Driftline recorded how much of the product works for a team that has no Intentset model (Driftline #27). The decision record, ADR-NO-MODEL, says a model is required but Intentset is not, because Driftline drafts a model itself. The value is a gradient, not a switch, and a drafted model stays labelled as a draft until a person accepts it. The record is still a draft. Driftline's check reports 33 artifacts, 0 errors and the same 21 warnings.
What it achieved
The decision is linked to the slice it shapes, SLICE-ADOPTION-LADDERS, so it counts as one of that slice's records: when the slice's code changes, updating the decision satisfies the drift gate, and the gate knows where to look. What Driftline's site says about working without Intentset now sits next to the code that has to deliver it.
2026-10-06: The gate goes on
Streamlane made its model a required check on every pull request (Streamlane #352). The check runs two commands through pnpm dlx, so the product gains no dependency:
intentset validate --level L2 --mode migration. L2The second of Intentset's adoption levels: records are checked against each other, and the code's structure is checked against the slices. checks the records and the code's structure. Migration modeA setting for adopting Intentset on existing code: violations already in the baseline are warnings, not errors. reports the violations that already existed as warnings, so only a new one fails.intentset review --fail-on-drift, the drift gate: it fails when a slice's code changed and its records did not, unless a commit says no behavior changed.
The pull request opened with 478 entries in the baseline:
- 452 for 226 imports that reach past another slice's entrypointThe one file through which other slices may use a slice. Reaching past it into a slice's other files is a violation., each reported twice (VSA003 and TS006Two rules that both report an import of another slice's private file: one from the slice rules, one from the TypeScript profile.).
- 21 for imports that cross a regionA broad part of the codebase, such as shared code or the app's shell, with rules about which way imports may go. the wrong way (REG001).
- 4 for imports of the backend's client library outside the one module meant to hold them (AMP002).
- 1 for a dependency cycleSlices that depend on each other in a loop, so none of them can change alone. (VSA005).
A commit in the same pull request routed the blocked slice's users through its entrypoint, which closed an item the first pilot had left open and removed 6 entries. The gate merged with 472. Streamlane's rule is to rewrite the baseline only when a change removes entries, never to add a new one.
The check on Streamlane's main branch then reported:
86 artifacts, 0 errors, 577 warnings
Architecture (L2, migration mode): 37 active slices; 192 files claimed,
0 unclaimed, 596 out of scope (counted, not reported); 1025 import edges,
487 across slices; 1 cycle; 0 excepted, 472 baselined, 0 stale baseline entries
What it achieved
- The structure stopped getting worse. From that day a change that adds a structural violation cannot merge. The 472 known ones are written down, and the list can only shrink.
- Records keep up with code. Every change to a slice's code now has to update its records or say why not.
What it cost
The gate's first failure came on its own pull request, on its second commit. That commit changed code in two slices, the blocked state and the work views, and said so in a sentence: Intentset-Unchanged: SLICE-WORK-BLOCKED's entrypoints and claims are as recorded; …. Review reads that trailerA Key: value line at the end of a commit message. as a list of slice IDs, so the sentence acknowledged neither slice:
Code changed, records unchanged (2, 2 not acknowledged)
If behavior changed, update those records in this change. If it did not,
say so in a commit with the trailer `Intentset-Unchanged: <slice ID>`.
It listed the 21 records that describe the blocked state, the ones the first pilot said the next change would meet. A commit with one trailer per slice, Intentset-Unchanged: SLICE-WORK-BLOCKED and Intentset-Unchanged: SLICE-WORK-VIEWS, passed, and Streamlane's instructions to its agents now say one trailer per slice. Every run of the check after that passed, on main and on the next four pull requests (Streamlane #353, #355, #356 and #357), to 2026-10-06.
2026-10-04 to 2026-10-06: Driftline, modelled before its code
Driftline's model was written before any of its product code (Driftline #6, 2026-10-04): 26 records, all draft, each claim citing its source. They are one product, 4 intents, 6 outcomes, 8 capabilities, 6 slices and 1 decision. The model passed L2 with 0 errors and 18 warnings, all expected of a model that runs ahead of its code: each draft slice pointed to files not yet written, and no slice had behaviors yet. Driftline added the check to its pull requests that evening (Driftline #8).
What it achieved
- The first code was checked against a plan written before it. The package that reads usage evidence was checked against the model in Driftline #20, which merged with 0 errors and no warnings on its slice.
- Records reached a person the same day. Gary approved the first records that afternoon (Driftline #21). The slice, its two behaviors and their two verification records became implemented, and the rule that evidence names no person became approved. The capability is still a draft.
What it cost
- No place for a measure. The 0.5 release had no record type for a success measure, so each outcome carried its measure in a section of its own. Measures became records in 0.6.0, released that evening (ADR 0012). Driftline's six outcomes have no measure records yet; that is six of its warnings today.
- TypeScript 7. The architecture check needs TypeScript 7 and Driftline is on 5.9, so the command-line tool is pinned in a package of its own.
2026-10-06: The loop closes at runtime
Streamlane's backend now says which behaviors each call delivers (Streamlane #351, ADR 0062). The code says it with one line, exercised(context, "BEH-…"), and each call writes one line of usage evidenceOne log line per call saying which behaviors the call delivered and whether it succeeded. It names no person. to the backend's log:
{"usage":"0.1","kind":"result","op":"<operation>","org":"<org id>","outcome":"invalid","behaviors":["BEH-BLOCKED-START-GUARD"],"rule":"RULE-BLOCKED-DEFINITION","ms":38}
A line carries the operation, an opaque organization id, the outcome, the behaviors, and the rule that refused the call, if one did. In ADR 0062's words, "It names no person: no user id, email, name, argument, message, item id or key." The format, usage evidence 0.1, is a draft written in Driftline under the MIT licence, meant to move into Intentset once it has proved itself.
Driftline's first capability reads it back (Driftline #20). It loads Streamlane's model with @intentset/core, finds which behaviors Streamlane's code names, and counts the evidence for each: attempts, successes, refusals by rule, failures and accounts. A behavior that nothing names reads "no signal", never "unused". It answers from the command line and from an MCP serverA standard way for AI assistants and tools to ask a program questions..
The read recorded on Driftline #20 covered one day of a shared test environment, where Streamlane's end-to-end tests made the calls. Each behavior's attempts came from 2 accounts, both test organizations. These are test calls, not real use.
| Behavior | Attempts | Succeeded |
|---|---|---|
| BEH-BLOCKED-START-GUARD | 34 | 34 |
| BEH-BLOCKED-REASON | 14 | 14 |
| BEH-BLOCKED-BY-LINK | 11 | 11 |
| BEH-BLOCKER-CLOSES | 2 | 2 |
| BEH-BLOCKED-SHOWN | no evidence | n/a |
Of the 3,066 lines read, 61 named a behavior. The other 3,005, across 74 operations, named none: those operations are not linked to behaviors yet.
What it achieved
- The product's logs can be read by behavior. For the first time, a question like "is anyone hitting the start guard?" is answered from the logs, counted per behavior, rather than by reading code.
- It showed a guard that is never refused. The browser disables Start on a blocked item, so a refusal reaches the backend only from another client, such as an agent or the command line. Streamlane's tests cover the refusal.
- It told apart unused from unmeasured. The blocked state's display is named only in the web app, so it has no backend evidence. The report says where it is named rather than calling it unused.
What it changed in Driftline
Two changes came from the first run: a behavior with no evidence now says where it is named, and the operations that name no behavior are cut to the busiest ten with the rest counted, so nothing is dropped silently. ADR 0062 names the second capability's 15 behaviors as the next ones to name in code.
2026-10-04: The model widens across Streamlane
Streamlane moved its web app into feature folders, by area and slice, without changing behavior (Streamlane #321, ADR 0058): 179 files into 35 feature folders and 4 shared ones. The architecture check's scopeThe files the check covers. Files outside it are counted but not checked. was set to match.
Validation went from 0 errors to 140: 134 files that no slice claimed, and 6 for 3 imports past the blocked slice's entrypoint. Half an hour later Streamlane drafted a slice record for each of the 34 folders that had none (Streamlane #324). Each draft claims its folder, lists the dependencies it found, and names the most-imported file as its entrypoint, for the owner to confirm or change. Unclaimed files fell to 0, and errors rose to 473: 448 for 224 imports past another slice's entrypoint, 20 for region crossings, 4 for imports of the backend's client library and 1 for a cycle. In Streamlane #324's words, "declaring every slice lets the checker see the boundaries between them, and the web app does not respect them yet."
Slices went from 1 to 37, and records from 26 to 86, between 2026-10-04 and 2026-10-06.
What it achieved
- The whole web app became visible to the check. With one slice declared, the check had seen only the imports into the blocked slice. With 37, it saw 224 imports past another slice's entrypoint that nobody had counted. Those became the baseline the gate holds today.
What it cost
- A miscount. The ADR first said 268 unclaimed files, because
grep -calso counted each error'sfix:line. Streamlane #324 corrected it to 134. - A false positive. Three warnings were the checker's mistake (TS004): a
/in JSX text read as the start of a regular expression. - An open question. Shared code that assembles slices crosses a region the wrong way, 21 times on that day, and ADR 0058 has not decided what to do about it. The 21 went into the baseline.
What it changed in Intentset
The import reader now reads JSX as JSX. The fix shipped in 0.6.0 that evening, and Streamlane moved to it the same night.
2026-10-04: Help from the model
Streamlane put four tips on its blocked-work knowledge record and bound each to the control that delivers its behavior (Streamlane #314). intentset publish writes help.json, and the web app shows each tip, through @intentset/help, on every element marked data-behavior="BEH-…".
What it achieved
- The help is tied to the behavior it explains. Approved for publication, the record is pinned to 5 behaviors and 3 rules, so a change to any of them sends it back for review instead of leaving stale help on screen.
- The first approval through Intentset. It was the first Streamlane record a person approved this way. Today the file carries four tips. BEH-BLOCKER-CLOSES has none, because no control delivers it: it happens when the blocker closes.
What it cost
The record was a draft, and publishing leaves drafts out by default, so help.json came out with no tips. Gary approved the record for publication before the change merged (Streamlane #315).
What it changed in Intentset
Tips on knowledge records, help.json and @intentset/help were released in 0.5.0 at noon that day (ADR 0011). The ADR was written from the pilot's knowledge record, which then explained four behaviors in three paragraphs: a panel, not a hover.
2026-10-02: The first pilot, one capability modelled
An agent modelled one capability of Streamlane, its blocked state, from what the repository already held (Streamlane ADR 0057). The product owner had not reviewed it. The pilot was measured on a branch on 2026-10-02 and reached Streamlane's main branch on 2026-10-04 (Streamlane #305), with the same result.
What was modelled
In Streamlane, a task or bug is blocked when another open item blocks it or someone gives a reason, and every screen, the start guard and the backend must agree on that answer. The capability spanned three packages, had tests at three levels, and had a decision record explaining its design. From those, the agent wrote:
Each of the eight verification records names a test that already existed. The agent also wrote the slice that owns the code and the contract it exposes, and the existing decision record joined the model by gaining a header, its text unchanged. Every record the agent wrote started as a draft; the decision record, already accepted, joined as approved.
What it achieved
The model validated, and its slice passed the architecture check. Reading it showed what no one had listed:
- Two of the three rules had no test: that the blocked state changes in the same transaction as its cause, and that a blocker hidden from the reader is counted but never named.
- Three behaviors were checked only in the browser, by end-to-end tests that need a deployed environment, so their evidence reads as missing until those run.
- Five of the eight checks passed on the commit the records describe: the core unit tests, and the backend tests against a local database.
None of this was new to the code. It was new to the people reading it.
What it changed in Intentset
The pilot asked four questions the specifications had not answered, and each was decided:
- A slice can span packages. The blocked state's logic lived in a shared package and its interface in the web app, so a slice now has one entrypoint per package it spans.
- Reaching past a slice's entrypoint is reported even from outside the checked scope, as a warning, so a narrow scope no longer hides it.
- Test results stay outside the commit they assess, because a commit cannot describe its own results.
- A draft rule with no test is a warning, so the gap shows before it becomes an error.
It also exposed defects in the reference implementation, which were fixed before release.
What came after
Tried against the 0.4 release, a one-line change to the slice's code was listed for review with the 21 records that describe it, which is what the next change to the blocked state would meet. It did, on 2026-10-06, when the gate went on.