Repository navigation
SSDs and Operation Contracts
| Noun phrase | Found in | Decision | Why |
|---|---|---|---|
| developer | Look Up Information Online, step 1 | Conceptual class: Developer
|
A real person with an identity |
| script | Look Up, step 1 | Neither | Software the developer writes, not a domain concept |
| agent / "the system" | Look Up, step 2 | Neither | This is the system being designed; a domain model doesn't include itself |
| question in plain English | Look Up, step 1 | Conceptual class: Task
|
The job being handed over; it has a status, steps and an answer |
| instructions | Submit Web Form, step 1 | Attribute of Task
|
Just the wording of the task |
| AI model | Look Up, step 4 | Conceptual class: AIModel
|
A named thing that can be swapped for another and has its own abilities |
| model provider (Google Gemini) | Look Up, stakeholders | Attribute of AIModel: provider |
A name |
| the account (its rate limits and quota) | Look Up, stakeholders | Conceptual class: ProviderAccount
|
The thing the key and the limits belong to; extensions 5a and 5b are about it |
| API key | Look Up, preconditions | Attribute of ProviderAccount: apiKey |
Text |
| rate limit / quota | Look Up, stakeholders | Attribute of ProviderAccount: requestsPerMinuteLimit |
A number |
| backup model | Look Up, 5a | Neither | An AIModel playing a role; it shows as a role name on the association |
| Chrome / browser | Look Up, step 2 | Neither | The program the system drives; what matters here is the page it shows |
| tab | Collect Data, 2a | Neither | Part of the browser program; which page is showing is covered by PageSnapshot
|
| page | Look Up, step 3 | Conceptual class: WebPage
|
A real thing with an address and a title |
| URL | Submit, step 2 | Attribute of WebPage: url |
Text |
| site / website | Collect Data, preconditions | Conceptual class: Website
|
Pages belong to it, and site rules are written against it |
| "captures the current page" | Look Up, step 3 | Conceptual class: PageSnapshot
|
What the model is actually shown at one step, which is not the live page |
| screenshot | Look Up, step 3; Submit, 1a and 3a | Conceptual class: Screenshot
|
A picture, not text or a number, and it can carry things the page text doesn't |
| element / numbered list of elements | Look Up, step 3 | Conceptual class: PageElement
|
A real thing on the page that can be clicked or typed into |
| the number on an element | Submit, step 3 | Attribute of PageElement: index |
A number |
| field / button | Submit, steps 3-6 | Attribute of PageElement: kind |
A kind of element, not a separate thing |
| form / contact form | Submit, step 1 | Conceptual class: Form
|
A real grouping of fields that gets submitted as one |
| value for a field | Submit, step 1 | Attribute of Action: textEntered |
Text |
| password / secret | Submit, 1a | Conceptual class: Secret
|
It has a name, is referred to by that name, and is supplied separately from the task |
placeholder <secret>name</secret>
|
Submit, 1a | Attribute of Secret: placeholder |
Text that stands in for the value |
| step | Look Up, step 7 | Conceptual class: Step
|
One round of look, decide and act, with its own record |
| evaluation / memory note / next goal | Look Up, step 5 | Attributes of Step
|
All three are text |
| action (input, click, navigate, done, extract) | Look Up, steps 5-6 | Conceptual class: Action
|
A real thing done to the browser; the individual names are values of its kind |
| error ("blocked by security policy") | Collect Data, 2a | Attribute of Action: result |
Text describing how the action ended |
| answer / final result | Look Up, step 9 | Conceptual class: Answer
|
What the developer receives; it may or may not exist |
| success | Look Up, 7b | Attribute of Answer: succeeded |
True or false |
| response page | Submit, step 7 | Neither | It is a WebPage, already in the model |
| history / record of the run | Look Up, success guarantee | Neither | The record is the Task with its Steps and Answer
|
| reviewer | Submit, stakeholders | Conceptual class: Reviewer
|
A real person who reads the record afterward |
| approved sites / allowed_domains | Collect Data, preconditions | Conceptual class: SiteRule
|
A rule saying which sites may be visited |
| pattern | Collect Data, 2a | Attribute of SiteRule: pattern |
Text |
| step limit | Look Up, 7b | Attribute of Task: stepLimit |
A number |
| requested format / schema | Collect Data, step 1 and 1a | Attribute of Task: answerFormat |
A description of how the answer should look |
| CAPTCHA | Look Up, 6a | Neither | A page the site shows, so a WebPage
|
| search engine | Look Up, 6a | Neither | A Website
|
| Markdown text, characters, batch | Collect Data, 4a | Neither | How extraction is carried out inside the system |
| quotes, authors, tags | Collect Data, step 1 | Neither | The contents of one particular answer |
Source: docs/domain-model.md
classDiagram
class Developer {
name
}
class Reviewer {
name
}
class Task {
instructions
status
answerFormat
stepLimit
}
class Secret {
name
value
placeholder
}
class AIModel {
provider
name
canSeeScreenshots
}
class ProviderAccount {
apiKey
plan
requestsPerMinuteLimit
}
class Step {
number
evaluation
nextGoal
}
class Action {
kind
textEntered
result
}
class PageSnapshot {
capturedAt
}
class Screenshot
class WebPage {
url
title
}
class Website {
domain
}
class Form
class PageElement {
kind
label
index
}
class SiteRule {
pattern
kind
}
class Answer {
text
succeeded
}
Developer "1" -- "0..*" Task : requests
Developer "1" -- "0..*" Secret : supplies
Developer "1" -- "0..*" SiteRule : sets
Reviewer "0..*" -- "0..*" Task : reviews
Task "0..*" -- "1" AIModel : primary model
Task "0..*" -- "0..1" AIModel : backup model
AIModel "0..*" -- "1" ProviderAccount : reached through
Task "1" -- "1..*" Step : carried out in
Task "1" -- "0..1" Answer : produces
Step "1" -- "1..*" Action : performs
Step "1" -- "1" PageSnapshot : looks at
PageSnapshot "0..*" -- "1" WebPage : of
PageSnapshot "1" -- "0..1" Screenshot : includes
PageSnapshot "1" -- "0..*" PageElement : lists
WebPage "0..*" -- "1" Website : belongs to
WebPage "1" -- "0..*" Form : contains
Form "1" -- "1..*" PageElement : has
Action "0..*" -- "0..1" PageElement : targets
Action "0..*" -- "0..1" Secret : enters
SiteRule "0..*" -- "0..*" Website : applies to
The model went from 8 classes to 16, and every addition came from a noun the use cases actually use. The biggest change is that "the page" split into three: WebPage is the live page with its address, Website is the site it belongs to (so SiteRule has something to apply to), and PageSnapshot is what the model is shown at one step. My verification runs forced that split. The captured text and the Screenshot carry different information, since the page text showed <secret>customer_email</secret> while the screenshot showed the real address, so a screenshot has to be its own class rather than a detail of the page. Secret, ProviderAccount, Form, SiteRule and Reviewer come from the preconditions and stakeholder lists I had written but never modeled, and Task picked up stepLimit and answerFormat from extensions 7b and 1a. User became Developer to match the primary actor in all three use cases. I left out the agent itself, Chrome, tabs and the run history: the first is the system being designed, the next two are the software it drives, and the history is just the Task with its steps and answer.
The system is one black box in every diagram, the Developer is the only actor, and every event is named as an operation with parameters taken from the domain model. Return values use dashed arrows.
sequenceDiagram
actor D as Developer
participant S as :BrowserUseSystem
D->>S: createTask(instructions, primaryModel, backupModel)
S-->>D: task status
D->>S: runTask(stepLimit)
S-->>D: task status
D->>S: getAnswer()
S-->>D: answer text, succeeded
sequenceDiagram
actor D as Developer
participant S as :BrowserUseSystem
loop for each secret the form needs
D->>S: supplySecret(name, value)
S-->>D: placeholder
end
D->>S: createTask(instructions, primaryModel, backupModel)
S-->>D: task status
D->>S: runTask(stepLimit)
S-->>D: task status
D->>S: getAnswer()
S-->>D: answer text, succeeded
sequenceDiagram
actor D as Developer
participant S as :BrowserUseSystem
loop for each approved site
D->>S: setSiteRule(pattern, kind)
end
D->>S: setAnswerFormat(answerFormat)
D->>S: createTask(instructions, primaryModel, backupModel)
S-->>D: task status
D->>S: runTask(stepLimit)
S-->>D: task status
D->>S: getAnswer()
S-->>D: answer text, succeeded
One contract per SSD. Every class and attribute named below appears in the domain model above.
Operation: createTask(instructions: Text, primaryModel: AIModel, backupModel: AIModel)
Cross-references: Use case Look Up Information Online
Preconditions:
- A Developer exists
- An AIModel exists to act as the primary model, reached through a ProviderAccount
whose apiKey is set
Postconditions:
- A Task instance was created
- Task.instructions was set to instructions
- Task.status was set to notStarted
- Task.answerFormat was set to plain text
- The Task was associated with the Developer
- The Task was associated with the primaryModel AIModel as primary model
- The Task was associated with the backupModel AIModel as backup model
Operation: runTask(stepLimit: Number)
Cross-references: Use case Submit Web Form
Preconditions:
- A Task exists with Task.status notStarted, associated with a Developer and with an
AIModel as primary model
- Every Secret named by a placeholder in Task.instructions exists and is associated
with that Developer
Postconditions:
- Task.stepLimit was set to stepLimit
- Task.status was set to finished
- Step instances were created
- Each Step was associated with the Task
- Step.number was set to its position in the run
- Step.evaluation was set to the model's judgement of the previous step
- Step.nextGoal was set to the model's stated goal for that step
- A PageSnapshot instance was created for each Step
- Each PageSnapshot was associated with its Step
- Each PageSnapshot was associated with the WebPage it was taken from
- A Screenshot instance was created and associated with each PageSnapshot
- PageElement instances were created and associated with their PageSnapshot
- Action instances were created and associated with their Step
- Action.kind was set to input for each field filled, and to click for the option
choices and the Submit button
- Action.textEntered was set to the value typed for each input Action
- Action.result was set to the outcome reported for that Action
- Each input Action was associated with the PageElement it targeted
- Each input Action that typed a secret was associated with that Secret
- An Answer instance was created
- Answer.text was set to the response the Website returned
- Answer.succeeded was set to true
- The Answer was associated with the Task
Operation: setSiteRule(pattern: Text, kind: Text)
Cross-references: Use case Collect Data From Website
Preconditions:
- A Developer exists
- No SiteRule with this pattern and kind is already associated with that Developer
Postconditions:
- A SiteRule instance was created
- SiteRule.pattern was set to pattern
- SiteRule.kind was set to kind, either allowed or prohibited
- The SiteRule was associated with the Developer
- The SiteRule was associated with each Website whose domain matches pattern
| SSD event | Comes from this use case step | Contract |
|---|---|---|
createTask(instructions, primaryModel, backupModel) |
Look Up step 1: creates an Agent with a question and a model | Contract 1 |
runTask(stepLimit) |
Look Up step 1 (run()), its steps 3-8, and 7b for the limit |
Contract 2 |
getAnswer() |
Look Up step 9: the script prints final_result()
|
- |
supplySecret(name, value) |
Submit Web Form 1a: the value goes in sensitive_data, not in the task text |
- |
setSiteRule(pattern, kind) |
Collect Data preconditions: the site must be on the approved list | Contract 3 |
setAnswerFormat(answerFormat) |
Collect Data 1a: the developer supplies a schema | - |
Tool: Claude Code in VS Code, running Claude Sonnet 5 and Claude Opus 5. When: September 2026, over several sessions ending September 11.
| Part | What I asked for | What the AI did |
|---|---|---|
| Part 1: Domain model | List the nouns in my three use cases and update the model | Classified 42 noun phrases as conceptual class, attribute or neither, with a reason for each. Rewrote the model from 8 classes to 16, adding Secret, ProviderAccount, Form, SiteRule, Website, PageSnapshot and Screenshot, and renaming User to Developer. Wrote docs/domain-model.md and rendered the PNG and SVG. |
| Part 2: SSDs | One SSD per fully-dressed use case | Wrote three sequence diagrams to docs/ssds.md with the Developer as the only actor and the system as one box, named every event as an operation with parameters from the domain model, added dashed returns and loop boxes, and rendered all three to PNGs to confirm the syntax parses. |
| Part 3: Contracts | A contract for one event from each SSD | Wrote createTask, runTask and setSiteRule to docs/contracts.md, each with Operation, Cross-references, Preconditions and Postconditions. Kept every postcondition in the three allowed forms and used only class and attribute names that exist in my model. Built a table mapping each SSD event to its use case step and contract. |
| Supporting work | Keep the pieces consistent and check my repo | Assembled the full wiki page from the source files so they can't drift apart, re-verified every code line number after my upstream merge and corrected three that had shifted, audited my wiki against all three assignment sheets, and advised on the commit and push. |
- Chose the three use cases to model and decided which ones were worth writing up as fully-dressed, along with which outcomes my project supports best and worst.
- Set up every API key myself (Google AI Studio, DeepSeek, browser-use Cloud) and managed
.env, including commenting keys in and out while testing providers. The keys were never shared or committed. - Ran the agent myself and brought the error output back for diagnosis, which is where the
403,402,429and404failures in my build notes came from. - Wrote my own first pass at how events move between the layers, which the AI then corrected.
- Created and edited every wiki page: Home, Project Outcome Analysis, Requirements and Use Cases, Build Demo Applied Analysis, and SSDs and Operation Contracts. I decided what belonged on which page and pasted the content in myself.
- Took and added the three fork-verification screenshots, and made the use case diagram image.
- Handled all the git work: created the
Build_notesbranch, synced my fork with upstream, mergedmaininto my branch, committedA2b: Add SSDs and operation contracts, and pushed/docstomain. - Checked the pull request I had opened against the upstream project before creating it, and closed it once I understood it would have sent my coursework to the maintainers.
- Reviewed each draft the AI produced and rejected or reworked the parts that didn't match what I wanted on the page.
-
It invented a class name. My outcome analysis said page elements were represented by
DOMElementNode. No such class exists; the real one isEnhancedDOMTreeNodeinbrowser_use/dom/views.py. Caught when the AI went to look up line numbers for the representational gap table and couldn't find the class it had named earlier. -
It overstated what CI does. It claimed a failing test blocks a merge. Checking the workflow
files showed only that
test.yamlandlint.ymlrun on every pull request. Whether a failure blocks merging is a GitHub branch-protection setting that isn't visible in the repository. -
It got the security check backwards. It said
SecurityWatchdogblocks a disallowed site before the page loads. I tested it withallowed_domains=['example.com'], and the page began loading first; the tab was then switched toabout:blank, with the "before navigation" warning logging last. The site stays blocked, but the request still reaches it. -
It described a step the system doesn't take. My Collect Data use case said the agent uses the
extractaction. Running it showed the agent read the quotes straight off the captured page and finished in two steps, never callingextract. It moved to an extension instead. -
It recommended a model that didn't work.
gemini-flash-latestresolved to an overloaded model that returned503, then429after five requests in a minute. Earlier it had switched my script toChatDeepSeekwithout checking that the class never readsDEEPSEEK_API_KEY, which produced an error telling me to setOPENAI_API_KEYinstead. -
It cited the wrong
doneaction. Line 2006 is the schema version ofdone; the plain one is at 2043. Caught while re-reading the file to write the contracts. -
Its line numbers went stale. After I merged 123 upstream commits, three citations had moved
(
EnhancedDOMTreeNode,final_result,_force_done_after_failure). Caught by re-checking every citation against the current checkout before publishing the page. - It assumed a fixed shape for a run. Its first draft of the Submit Web Form scenario fixed how many actions happen per step. Two live runs grouped them differently, four steps once and five the other time, so the scenario had to stop depending on the grouping.
Created for this assignment (all committed in A2b: Add SSDs and operation contracts):
-
docs/ssds.mdβ the three system sequence diagrams -
docs/contracts.mdβ the three operation contracts -
docs/wiki-ssds-and-contracts.mdβ the assembled wiki page, 280 lines -
docs/ssd-1-look-up.png,docs/ssd-2-submit-form.png,docs/ssd-3-collect-data.png -
docs/domain-model.pnganddocs/domain-model.svgβ re-rendered for the 16-class model
Changed for this assignment:
-
docs/domain-model.mdβ rewritten from 8 classes to 16
Created for earlier assignments:
-
docs/build-notes.md,docs/ai-use-log.md -
docs/demo-backup.gifβ a recording of a successful run, kept local only because.gitignoreexcludes*.gif
Changed earlier:
-
examples/getting_started/01_basic_search.pyβ switched to Gemini with a backup model, takes a task from the command line, prints the final answer (committed in theA2commit) -
examples/getting_started/04_multi_step_task.pyβ the same Gemini switch, still uncommitted
Not the AI's work: agent.py in the repository root is mine, and it remains untracked.