Kratos Labs · Field note

Build the company workspace: from three files to one repeatable AI workflow

Turn a three-file AI company OS into one repeatable workflow with canonical sources, skills, Git history, failure-tested checks, and exact-version approval.

Part 2 of 3. Add one owner file, one complete procedure, Git history, learning ledgers, a checker that can fail, and approval tied to one exact artifact.

Part 1 built the smallest useful company workspace:

Codetext
company-os/  CLAUDE.md  AGENTS.md  OPERATING.md  knowledge/    memory.md  outputs/    README.md

Keep CLAUDE.md if you use Claude Code, AGENTS.md if you use Codex, or both if you use both hosts. Each is only an automatic entry point into the same canonical OPERATING.md contract. It is not a second copy of the rules.

That version solves orientation. A fresh AI session knows where the rules live, where current context lives, where drafts go, and which actions still require the owner.

It does not yet solve repeatability.

Ask two fresh sessions to prepare the same client update and they may choose different sources, output names, and definitions of finished. Both can sound intelligent while one skips an open risk, reads an old draft, or overwrites last week's work.

This article fixes that problem. We will turn the three-file workspace into one complete workflow that behaves the same way across fresh sessions.

The example is deliberately ordinary:

Codetext
Prepare the weekly client update for Example Client.

By the end, that request will follow one written procedure, read one official client record and one dated work note, and save to one predictable path. It will stop at the same approval boundary and pass a checker you have deliberately watched fail.

I use artifact to mean a saved work product such as a draft, report, or generated document. It is a useful short label, but saving an artifact does not make its claims current truth.

Read Part 1 first if you have not built the three-file workspace. Part 3 adds the reliability layer used across history, schedules, AI tools, and workers.

What we are adding, and why

The finished starter will look like this:

Codetext
company-os/  CLAUDE.md  AGENTS.md  OPERATING.md  knowledge/    memory.md    clients/      example-client/        status.md  outputs/    README.md    client-updates/      .gitkeep    work-logs/      example-client-2026-08-11.md  skills/    weekly-client-update/      SKILL.md  ai/    DECISIONS.md    ERRORS.md    TOMBSTONES.md  scripts/    check-retired-value.sh  fixtures/    known-bad-retired-value.md    clean-value.md    clock-cases.md

The new pieces each answer a different question:

  • status.md: What is true about this client now?
  • The dated work log: What work happened during the reporting week?
  • SKILL.md: How should this repeated job run?
  • Git: What changed, and why?
  • The three ledgers: What was decided, what failed, and what must not return?
  • The checker and fixtures: Can one real rule be tested, including its failure state?
  • The deadline fixture: Can you distinguish a live overdue item, a resolved item, and a bad date before pretending you have an automated clock?

This is still a small system. The difference is that its behavior is now testable.

Confirm the host and terminal prerequisites

This guide assumes a file-aware AI host that can open the folder, edit files, and run terminal commands after you approve them. Claude Code, Codex, and Cursor are examples.

You also need Git and a shell that provides awk, grep, mktemp, and chmod. The commands below target macOS, Linux, and Git Bash.

Run one block at a time. Stop if any command reports an error. Before running a command you do not understand, ask your coding agent to explain what it reads, what it writes, and how to undo it.

If you are starting from an empty folder, run:

Codesh
git --version || exit 1mkdir company-os || exit 1cd company-os || exit 1git init || exit 1pwdgit status

Confirm that pwd ends in /company-os before creating files.

If you already completed Part 1, open a terminal inside that existing company-os folder and run:

Codesh
git init || exit 1pwdgit status

Now check the remaining prerequisites:

Codesh
git config user.namegit config user.emailcommand -v awkcommand -v grepcommand -v mktempcommand -v chmod

If either Git identity command prints nothing, set repository-local values before your first commit:

Codesh
git config user.name "Your Name"git config user.email "you@example.com"

Replace those values with your real Git identity. Save shell scripts with LF line endings.

Create the new directories and files:

Codesh
mkdir -p knowledge/clients/example-client || exit 1mkdir -p outputs/client-updates || exit 1mkdir -p outputs/work-logs || exit 1mkdir -p skills/weekly-client-update || exit 1mkdir -p ai || exit 1mkdir -p scripts || exit 1mkdir -p fixtures || exit 1touch OPERATING.mdtouch CLAUDE.mdtouch AGENTS.mdtouch knowledge/memory.mdtouch knowledge/clients/example-client/status.mdtouch outputs/README.mdtouch outputs/client-updates/.gitkeeptouch outputs/work-logs/example-client-2026-08-11.mdtouch skills/weekly-client-update/SKILL.mdtouch ai/DECISIONS.mdtouch ai/ERRORS.mdtouch ai/TOMBSTONES.md

Git does not record empty directories. The .gitkeep file preserves outputs/client-updates/ before the first update exists. The name has no special Git behavior. It is simply a small tracked placeholder.

All dates in the following example files are test data. Replace them before treating the files as current business records. Fixed dates are kept inside the fixtures because fixtures are deliberately stable tests.

Make the client status one official record

Put this in knowledge/clients/example-client/status.md:

Codemarkdown
# Example Client status ## Current state Stage: discoveryOwner: [name or role]Last updated: 2026-08-11 ## Current scope - Confirm the problem to solve.- Collect the source material needed for a weekly update. ## Open risks - The preferred update audience has not yet been confirmed. ## Decisions needed - Confirm whether the update is internal or customer-facing before it is sent. ## Next work - Prepare a source-backed weekly update draft. **Due:** 2026-08-15 - confirm the update audience.

Then put this pointer in the owner-map section of knowledge/memory.md:

Codemarkdown
### Example Client Current scope, stage, dates, risks, deal status, and current terms are owned by knowledge/clients/example-client/status.md.

Do not copy Stage: discovery into the startup map. The pointer names the domain and the official file without creating a second current-looking value.

Test the ownership boundary. Change the stage in status.md from discovery to delivery, then ask a fresh session:

Codetext
What is Example Client's current stage, and which file owns that answer?

Pass if the host follows the pointer and only status.md needs the factual change. Restore the example stage you want before continuing.

Give the workflow one dated piece of evidence

A weekly update cannot prove completed work if its only source is a current-status summary. Give the workflow one small dated record of what happened.

Put this in outputs/work-logs/example-client-2026-08-11.md:

Codemarkdown
# Example Client work log Client folder label: example-clientWork date: 2026-08-11 ## Completed - Drafted the first problem statement.- Collected the existing project notes into one review folder. ## Observed - The intended audience for weekly updates is still unconfirmed. ## Not completed - No customer-facing update was sent.

This is a dated work product, not the owner of current client state. The client status file still owns the stage, risk, and next work. The work log proves what happened during one period.

The distinction matters. A polished weekly update should not be allowed to invent completed work from a vague status sentence. It needs an in-period source.

Write one complete procedure

A skill is a written procedure for a repeated job. It says when the job applies, what it needs, which sources it reads, what it writes, where it stops, and what proves completion.

Add this bullet to the Startup section of OPERATING.md:

Codemarkdown
- For repeated work, inspect skills/ for a SKILL.md whose description contains a matching trigger phrase. Read the full matching skill before drafting. If no skill matches, say so, use the available facts carefully, and state what is missing instead of guessing.

Do not paste this into every chat. The CLAUDE.md or AGENTS.md entry file from Part 1 already directs every fresh session into OPERATING.md, so changing the canonical contract once changes the startup path for every later session.

Now put this in skills/weekly-client-update/SKILL.md:

Codemarkdown
---name: weekly-client-updatedescription: "Prepare a weekly client update from the official status file and dated work records. Trigger on: weekly update; client progress summary; Friday client recap; prepare this week's client update."--- # Weekly client update ## Inputs - Exact client folder label under knowledge/clients/.- End date for the reporting week.- Intended audience: internal owner or external client. ## Client matching - Use the exact directory name under knowledge/clients/ as the client folder label.- For Example Client, use example-client.- Stop if no folder matches or more than one folder could match. ## Sources 1. Read OPERATING.md.2. Read knowledge/memory.md.3. Read knowledge/clients/<client-folder-label>/status.md.4. Read only dated work records for the same client whose date falls inside the reporting week.5. Read ai/DECISIONS.md, ai/ERRORS.md, and ai/TOMBSTONES.md only if the update touches a settled decision, a prior failure, or a retired value. ## Steps 1. Confirm the client folder label, reporting-week end date, and intended audience.2. Separate completed work, open risks, decisions needed, and next work.3. Trace each completed-work claim to a dated source from the reporting week.4. Trace each current-state claim to the client's status owner.5. Do not promote a draft or older work record into current truth.6. Draft the update in clear language for the intended audience.7. Save it as outputs/client-updates/<client-folder-label>-week-ending-YYYY-MM-DD.md.8. If that path already exists, do not overwrite it without approval. Create an explicit revision such as -v2.md.9. List missing facts and unverified dates below the draft. ## Stop conditions - Stop and ask if the client identity is unresolved.- Stop and ask if the status owner is missing.- Stop and ask if no dated evidence supports a completed-work claim.- Do not invent a date, promise, approval, or completed action.- Do not send the update without explicit approval for the exact artifact version. ## Allowed writes - outputs/client-updates/- The client status owner only when the task explicitly supplies a real current-state correction. ## Proof - Every completed-work claim names a dated source file.- Every current-state claim agrees with the status owner.- The draft is saved at the deterministic output path.- State "Ready for owner review" unless the exact artifact version has explicit send approval.

The three lines between the opening and closing --- markers tell the host what the skill is called and which requests should trigger it. The rest is the full procedure. A discovery summary should point to this file, never replace it.

Run the workflow twice

Open a fresh session and ask:

Codetext
Prepare the weekly client update for Example Client for the week ending 2026-08-11. Use the client folder label example-client. The audience is the internal owner.

The worker should read:

  1. OPERATING.md
  2. knowledge/memory.md
  3. knowledge/clients/example-client/status.md
  4. outputs/work-logs/example-client-2026-08-11.md
  5. The matching skill

It should save:

Codetext
outputs/client-updates/example-client-week-ending-2026-08-11.md

The draft should say that the first problem statement and note collection were completed, that the audience remains unconfirmed, and that nothing was sent. It should end in Ready for owner review.

Run the same request in a second fresh session.

Pass if both sessions use the same official status file, dated evidence, output path, stop conditions, and approval rule. The sentences can differ. The operating behavior should not.

If the first draft already occupies the path, the second session should stop or create a clearly named revision only after you approve that choice. Silent overwrite is a failure.

Bind approval to one exact artifact

Approval should not float above a filename after the file changes.

When you approve an external version, record:

Codetext
Approved artifact: outputs/client-updates/example-client-week-ending-2026-08-11.mdContent hash: [output of git hash-object for that file]Approver: [name]Approval date: [verified date]

Generate the content hash with:

Codesh
git hash-object outputs/client-updates/example-client-week-ending-2026-08-11.md

Save the approval record beside the artifact as:

Codetext
outputs/client-updates/example-client-week-ending-2026-08-11.approval.md

Do not put the hash inside the artifact it hashes. That would change the content and invalidate the hash. A sidecar is a small separate file kept beside the artifact. It records approval for the target without changing the target.

Any edit changes the hash and returns the artifact to Ready for owner review. Mark the old approval sidecar as invalid or replace it only after the new version is reviewed. The approval applies to the exact content, not to every future file with the same name.

For this tutorial, do not send the sample update. The internal audience request ends at review.

Add Git history without sweeping in unrelated work

Git records snapshots of the folder. It lets the next worker answer what changed, when it changed, and why a rule exists.

Git does not make every file authoritative. It preserves history. Your owner files still decide what is current.

Staging is Git's list of exact file versions selected for the next commit. A file can be changed in the folder without being staged, which is why the staged list needs its own inspection.

Inspect the folder:

Codesh
git status

Stage only the starter files you intend to commit:

Codesh
git add OPERATING.mdgit add CLAUDE.mdgit add AGENTS.mdgit add knowledge/memory.mdgit add knowledge/clients/example-client/status.mdgit add outputs/README.mdgit add outputs/client-updates/.gitkeepgit add outputs/work-logs/example-client-2026-08-11.mdgit add skills/weekly-client-update/SKILL.mdgit add ai/DECISIONS.mdgit add ai/ERRORS.mdgit add ai/TOMBSTONES.md

Check the staged list:

Codesh
git status

If an accidental new file appears before the first commit, remove it from the staged set with:

Codesh
git rm --cached -- path/to/accidental-file

After the repository has at least one commit, use this for an accidentally staged path:

Codesh
git restore --staged -- path/to/accidental-file

Run git status again after either correction.

Create the first commit:

Codesh
git commit -m "Create repeatable weekly update workflow"

Pass if the commit contains only the files you meant to add. The worker should be able to use the history to answer what changed, why it changed, and which source or incident caused it.

Local Git protects history on this computer. It is not a backup. Before relying on the workspace, add a private remote or an encrypted backup and test that you can restore it. A configured backup schedule is not proof that a restore works.

Give decisions, failures, and retired values different homes

The next three files are ledgers. Here, a ledger means one running list kept in one file, with one row per event.

They are separate because a decision, a mistake, and an old value need different future behavior.

Decision ledger

Put this in ai/DECISIONS.md:

Codemarkdown
# Decision ledger This ledger primarily records settled rulings. A row explicitly labeled proposed remains analysis and does not govern work. | Date | Decision | Prediction to check | Review on | Outcome ||---|---|---|---|---|| 2026-08-11 | Use a written weekly update instead of a standing status meeting for this test. | Open questions decline without increasing missed commitments. | 2026-09-11 | |

The prediction and review point matter. Without them, We prefer weekly updates becomes a permanent preference with no way to learn whether it worked.

Error ledger

Put this in ai/ERRORS.md:

Codemarkdown
# Error ledger | Date | Failure | Why it happened | Immediate fix | Control that now prevents it ||---|---|---|---|---|| 2026-08-11 | A draft repeated an old project stage. | The writer used an output instead of the status owner. | Corrected the draft and the owner pointer. | The skill now requires the status owner, and review checks current-state claims against it. |

The error row does not assign blame. It connects an observed failure to the new behavior that prevents the same class of failure.

Tombstone ledger

Put this in ai/TOMBSTONES.md:

Codemarkdown
# Tombstone ledger | Retired value | Retired on | Current authority | Reason ||---|---|---|---|| OLD-PLAN | 2026-08-11 | knowledge/clients/example-client/status.md | This label was replaced and must not appear as a current plan. |

A tombstone does not erase history. It marks a string or decision that must not return as current. If an old current-looking file still contains the value, mark that occurrence visibly historical and point to the replacement.

Stage and commit the ledgers as one behavior change:

Codesh
git add ai/DECISIONS.md ai/ERRORS.md ai/TOMBSTONES.mdgit statusgit commit -m "Add decision error and tombstone ledgers"

Turn one real failure into a check

Do not build a universal policy engine. Mechanize one failure you understand.

The starter check reads retired strings from ai/TOMBSTONES.md and rejects one current-looking target file if it finds a match.

Before running the script, ask your coding agent to explain each command and confirm that it reads only the tombstone ledger and the declared target. This script is a simplified starter, not the exact Kratos checker.

Save this as scripts/check-retired-value.sh using literal ASCII spaces:

Codesh
#!/bin/shset -u tombstone_file='ai/TOMBSTONES.md'target_file=${1:-} if [ -z "$target_file" ]; then  echo "CHECKER ERROR: target file argument is missing"  exit 2fi if [ ! -f "$tombstone_file" ] || [ ! -r "$tombstone_file" ]; then  echo "CHECKER ERROR: tombstone ledger is missing or unreadable"  exit 2fi if [ ! -f "$target_file" ] || [ ! -r "$target_file" ]; then  echo "CHECKER ERROR: target file is missing or unreadable"  exit 2fi patterns_file=$(mktemp) || {  echo "CHECKER ERROR: could not create a temporary pattern file"  exit 2}trap 'rm -f "$patterns_file"' 0 1 2 3 15 if ! awk -F '|' '  /^[[:space:]]*\|/ {    value = $2    gsub(/^[[:space:]]+/, "", value)    gsub(/[[:space:]]+$/, "", value)    if (value == "" || value == "Retired value" || value ~ /^:?-+:?$/) next    print value  }' "$tombstone_file" > "$patterns_file"; then  echo "CHECKER ERROR: could not read the tombstone ledger"  exit 2fi if grep -n -F -f "$patterns_file" -- "$target_file"; then  echo "FAIL: retired value found in $target_file"  exit 1else  grep_status=$?fi if [ "$grep_status" -eq 1 ]; then  echo "PASS: no retired value found in $target_file"  exit 0fi echo "CHECKER ERROR: grep could not inspect $target_file"exit 2

A script reports a number when it finishes, called an exit code:

Codetext
0 = no retired value found1 = a retired value was found2 = the checker itself failed, so nothing was verified

Exit 2 must remain separate. A broken smoke alarm does not prove the house is safe.

Make the script executable and check its shell syntax:

Codesh
chmod +x scripts/check-retired-value.shsh -n scripts/check-retired-value.sh

Put this in fixtures/known-bad-retired-value.md:

Codemarkdown
# Deliberately broken fixture This file contains OLD-PLAN on purpose so the checker has something to reject.

Put this in fixtures/clean-value.md:

Codemarkdown
# Clean fixture No retired label appears in this file.

Run the four behavior tests:

Codesh
scripts/check-retired-value.sh fixtures/known-bad-retired-value.mdecho $?

Expected: 1.

Codesh
scripts/check-retired-value.sh fixtures/clean-value.mdecho $?

Expected: 0.

Codesh
scripts/check-retired-value.shecho $?

Expected: 2.

Codesh
scripts/check-retired-value.sh fixtures/does-not-exist.mdecho $?

Expected: 2.

The known-bad failure is success for the test. It proves the checker can expose its target mistake. The missing-argument and missing-target cases prove checker failure cannot collapse into a clean result.

Before and after the tests, run:

Codesh
git hash-object ai/TOMBSTONES.mdgit hash-object fixtures/known-bad-retired-value.mdgit hash-object fixtures/clean-value.md

Pass if each file's hash is unchanged. Also check that no temporary pattern file remains in the repository:

Codesh
git status --short

The script may create a temporary file in the operating system's temporary directory while it runs. Its exit trap removes that file. The important repository property is that neither source nor fixture is rewritten.

If the known-bad fixture returns 0, stop. The most common cause is that ai/TOMBSTONES.md does not contain the OLD-PLAN row, so the checker has an empty retired-value list and nothing to find. Confirm the row exists, then rerun.

Make the local commit hook earn trust

A pre-commit hook runs before Git creates a commit. The starter hook guards knowledge/memory.md against retired strings.

The checker reads a working file. To make that equivalent to checking the staged version, the hook first refuses to run if its two inputs have unstaged changes.

If .git/hooks/pre-commit already exists, do not overwrite it. Add the following logic to the existing hook and preserve its current checks. In a new starter repository, create .git/hooks/pre-commit with:

Codesh
#!/bin/shset -u git diff --quiet -- ai/TOMBSTONES.md knowledge/memory.md || {  echo "CHECKER ERROR: checker inputs have unstaged changes"  exit 2} scripts/check-retired-value.sh knowledge/memory.md

Then run:

Codesh
chmod +x .git/hooks/pre-commitsh -n .git/hooks/pre-commit

Test the hook once before trusting it.

  1. Add the line OLD-PLAN anywhere in knowledge/memory.md.
  2. Stage that exact file.
  3. Try to commit it.
Codesh
git add knowledge/memory.mdgit commit -m "Test retired value hook"

Pass if the commit is blocked and the checker prints the matching line.

Now remove OLD-PLAN from knowledge/memory.md, update the staged version, and inspect the staged set:

Codesh
git add knowledge/memory.mdgit status

Stage the real checker and fixture files:

Codesh
git add scripts/check-retired-value.shgit add fixtures/known-bad-retired-value.mdgit add fixtures/clean-value.mdgit statusgit commit -m "Add retired value check and fixtures"

Pass if the clean commit goes through. You have now observed both sides of the hook: red on a retired value, green on a clean staged state.

This hook is local Git configuration. Git does not share .git/hooks/ when another person clones the repository. Part 3 explains how Kratos separates tracked checker logic from machine-local activation and how a push gate protects the history that leaves the machine.

Add a manual deadline view before claiming an automated clock

Put deadlines beside the work they govern, in one exact starter format:

Codemarkdown
**Due:** YYYY-MM-DD - what must happen.

Kratos's live parser also recognizes a line-start Due: marker, but the starter uses the bold form above so one format is easy to learn.

Resolve a live item by striking through the Due marker itself. A nearby note that says done does not make a still-live token disappear.

Put these stable test cases in fixtures/clock-cases.md:

Codemarkdown
# Clock cases **Due:** 2020-01-01 - deliberately overdue test item. ~~**Due:** 2020-01-01 - deliberately resolved test item.~~ **Due:** 2026-99-99 - deliberately malformed test date.

Find declared markers with:

Codesh
grep -R -n -F '**Due:**' knowledge/ fixtures/clock-cases.md

This is a visibility search, not a clock. It locates markers but cannot classify dates, understand strikethrough, or validate a calendar date.

For the manual prototype, classify each returned marker:

  • OVERDUE: the marker is live and its valid date is before today.
  • RESOLVED: the marker itself is visibly struck through.
  • PARSE ERROR: the text looks like a marker but its date is not a real YYYY-MM-DD calendar date.

Pass if you classify the three fixture lines as overdue, resolved, and parse error. Do not claim the grep command did that work. When this becomes a recurring need, replace the manual classification with a tested parser that reads the owner files without changing them.

A stale date is a separate marker for perishable research, such as web-sourced pricing, that says when the claims must be checked again before reuse. Part 3 shows why due dates, stale dates, and decision reviews should feed one generated view rather than three task databases.

Stage and commit the deadline fixture:

Codesh
git add fixtures/clock-cases.mdgit statusgit commit -m "Add manual deadline classification fixture"

Write the approval ladder before adding autonomy

Copy these action classes and rules into OPERATING.md in whatever format fits your file:

Codetext
Read and diagnose: allowed inside the requested task. Draft and reversible company-folder changes: allowed only inside the files and folders the task owns. Commit owned files: allowed after the relevant checks and staged-list inspection. External communication: requires explicit approval for the exact artifact version. Spending and irreversible action: requires explicit approval plus stronger verification. Sensitive data: restricted to an approved local-only process and never copied into ordinary tracked files.

Reversible company-folder changes means an edit can be inspected and recovered through Git. It does not make an external message, purchase, filing, or commercial change reversible.

The approval rule is short enough to repeat:

Codetext
The AI may prepare and verify work. The owner must explicitly approve the specific version before it is sent, published, spent, or used to make an irreversible external change.

Verification and authority remain separate even when the workflow becomes more automated.

What you have built

Follow the weekly-update request from start to finish:

Codetext
ordinary request-> automatic host entry point-> operating contract-> startup map-> client status owner-> dated work evidence-> weekly-update skill-> deterministic output path-> source-backed draft-> exact-version approval boundary-> exact-path Git history-> checker and known-bad fixture

This is no longer a folder of prompts.

Two fresh sessions can find the same procedure. They can read the same official state and the same dated evidence. They can save to the same path. They know when to stop. A retired-value checker has returned 1, 0, and 2 for three different meanings. The pre-commit hook has blocked a bad staged state and allowed a clean commit.

The system is small, but the behavior is reproducible.

The next limit is reliability beyond the current folder

The starter still has important limits.

The local hook is not shared automatically. A clean current folder can hide a bad earlier commit. A scheduled backup can claim to run while every restore fails. A second AI tool may never discover the skill. Two workers can edit or stage each other's files. A green technical check still cannot decide whether a client message should leave the company.

Those are not reasons to build a giant orchestration platform. They are the exact boundaries the third article addresses.

Part 3 follows the real Kratos reliability layer. It explains why checks need three outcomes and why pre-commit and pre-push protect different surfaces. It shows how to scan every transferred commit, keep dates beside their owner files, turn corrections into lasting safeguards, and keep several AI tools on one procedure. It also shows how independent reviewers work without inheriting the writer's defense.

It also includes the honest audit that matters most: what is live in Kratos now, what remains deliberately narrow, and what has not been built.

Continue to Part 3: Make the company workspace reliable with checks, history, time, and human approval.

Series complete. Return to Part 1 for the foundation, or continue to Part 3 for the reliability layer.

Continue reading